diff options
| author | Mark Brown <broonie@kernel.org> | 2026-08-21 14:23:06 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-08-21 14:23:08 +0100 |
| commit | 1bb70cd208d68d056aac8dcde8b587e70d33c582 (patch) | |
| tree | 589879dc2e22e55bbf89485fc4fbea3b35437bb8 /drivers | |
| parent | 417c5a2b0966fccf49628301d1af61b23e0c390f (diff) | |
| parent | 8992f32c57607bdfaf5de2a2cd26b3b71f3a9d55 (diff) | |
| download | linux-next-1bb70cd208d68d056aac8dcde8b587e70d33c582.tar.gz linux-next-1bb70cd208d68d056aac8dcde8b587e70d33c582.zip | |
Merge branch 'char-misc-next' of https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
Diffstat (limited to 'drivers')
652 files changed, 24036 insertions, 21061 deletions
diff --git a/drivers/accessibility/speakup/fakekey.c b/drivers/accessibility/speakup/fakekey.c index 868c47b2a59b..9c0b2403876a 100644 --- a/drivers/accessibility/speakup/fakekey.c +++ b/drivers/accessibility/speakup/fakekey.c @@ -71,9 +71,9 @@ void speakup_fake_down_arrow(void) input_sync(virt_keyboard); __this_cpu_write(reporting_keystroke, false); - /* reenable preemption */ + /* re-enable preemption */ preempt_enable(); - /* reenable keyboard interrupts */ + /* re-enable keyboard interrupts */ local_irq_restore(flags); } diff --git a/drivers/accessibility/speakup/genmap.c b/drivers/accessibility/speakup/genmap.c index 0882bab10fb8..a9e308bdd0be 100644 --- a/drivers/accessibility/speakup/genmap.c +++ b/drivers/accessibility/speakup/genmap.c @@ -71,7 +71,6 @@ main(int argc, char *argv[]) open_input(NULL, argv[1]); while (fgets(buffer, sizeof(buffer), infile)) { - lc++; value = shift_state = 0; cp = strtok(buffer, delims); @@ -153,7 +152,7 @@ main(int argc, char *argv[]) continue; printf("\n\t%d,", lc); for (i = 0; i < max_states; i++) - printf(" %d,", (unsigned int)kp[i]); + printf(" %u,", (unsigned int)kp[i]); } printf("\n\t0, %d\n", map_ver); diff --git a/drivers/accessibility/speakup/i18n.c b/drivers/accessibility/speakup/i18n.c index d62079b1661f..554bf81f2c1c 100644 --- a/drivers/accessibility/speakup/i18n.c +++ b/drivers/accessibility/speakup/i18n.c @@ -31,7 +31,7 @@ static char *speakup_default_msgs[MSG_LAST_INDEX] = { [MSG_CURSORING_OFF] = "cursoring off", [MSG_CURSORING_ON] = "cursoring on", [MSG_HIGHLIGHT_TRACKING] = "highlight tracking", - [MSG_READ_WINDOW] = "read windo", + [MSG_READ_WINDOW] = "read window", [MSG_READ_ALL] = "read all", [MSG_EDIT_DONE] = "edit done", [MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset", diff --git a/drivers/accessibility/speakup/keyhelp.c b/drivers/accessibility/speakup/keyhelp.c index 822ceac83068..0940f430ac0f 100644 --- a/drivers/accessibility/speakup/keyhelp.c +++ b/drivers/accessibility/speakup/keyhelp.c @@ -8,14 +8,15 @@ */ #include <linux/keyboard.h> +#include <linux/ctype.h> #include "spk_priv.h" #include "speakup.h" #define MAXFUNCS 130 #define MAXKEYS 256 static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1; -static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS]; -static u_short masks[] = { 32, 16, 8, 4, 2, 1 }; +static u16 key_offsets[MAXFUNCS], key_data[MAXKEYS]; +static u16 masks[] = { 32, 16, 8, 4, 2, 1 }; static short letter_offsets[26] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -49,7 +50,7 @@ static int cur_item, nstates; static void build_key_data(void) { u_char *kp, counters[MAXFUNCS], ch, ch1; - u_short *p_key, key; + u16 *p_key, key; int i, offset = 1; nstates = (int)(state_tbl[-1]); @@ -111,7 +112,7 @@ static void say_key(int key) spk_msg_get(MSG_KEYNAMES_START + (key - 1))); } -static int help_init(void) +static void help_init(void) { char start = SPACE; int i; @@ -120,21 +121,27 @@ static int help_init(void) state_tbl = spk_our_keys[0] + SHIFT_TBL_SIZE + 2; for (i = 0; i < num_funcs; i++) { char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i); + char first_letter; - if (start == *cur_funcname) + first_letter = tolower(*cur_funcname); + + /* Accept only 'a'..'z' to index letter_offsets[] safely */ + if (first_letter < 'a' || first_letter > 'z') + continue; + + if (start == first_letter) continue; - start = *cur_funcname; + start = first_letter; letter_offsets[(start & 31) - 1] = i; } - return 0; } -int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key) +int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key) { int i, n; char *name; u_char func, *kp; - u_short *p_keys, val; + u16 *p_keys, val; if (letter_offsets[0] == -1) help_init(); @@ -144,7 +151,7 @@ int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key) synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP)); return 1; } - ch |= 32; /* lower case */ + ch = tolower(ch); if (ch < 'a' || ch > 'z') return -1; if (letter_offsets[ch - 'a'] == -1) { diff --git a/drivers/accessibility/speakup/kobjects.c b/drivers/accessibility/speakup/kobjects.c index 0dfdb6608e02..a73b478e06de 100644 --- a/drivers/accessibility/speakup/kobjects.c +++ b/drivers/accessibility/speakup/kobjects.c @@ -92,13 +92,13 @@ static void report_char_chartab_status(int reset, int received, int used, if (reset) { pr_info("%s reset to defaults\n", object_type[do_characters]); } else if (received) { - len = snprintf(buf, sizeof(buf), - " updated %d of %d %s\n", - used, received, object_type[do_characters]); + len = scnprintf(buf, sizeof(buf), + " updated %d of %d %s\n", + used, received, object_type[do_characters]); if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", - rejected, rejected > 1 ? "s" : ""); + rejected, str_plural(rejected)); pr_info("%s", buf); } } @@ -120,7 +120,7 @@ static ssize_t chars_chartab_store(struct kobject *kobj, ssize_t retval = count; unsigned long flags; unsigned long index = 0; - int charclass = 0; + u16 charclass = 0; int received = 0; int used = 0; int rejected = 0; @@ -461,7 +461,7 @@ static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr, struct st_var_header *p_header; struct punc_var_t *var; struct st_bits_data *pb; - short mask; + u16 mask; unsigned long flags; p_header = spk_var_header_by_name(attr->attr.name); @@ -740,7 +740,7 @@ static void report_msg_status(int reset, int received, int used, if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", - rejected, rejected > 1 ? "s" : ""); + rejected, str_plural(rejected)); pr_info("%s", buf); } } diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c index 78a77dd789a2..e9b7c2761f6f 100644 --- a/drivers/accessibility/speakup/main.c +++ b/drivers/accessibility/speakup/main.c @@ -63,7 +63,7 @@ int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10; int spk_no_intr, spk_spell_delay; int spk_key_echo, spk_say_word_ctl; int spk_say_ctrl, spk_bell_pos; -short spk_punc_mask; +u16 spk_punc_mask; int spk_punc_level, spk_reading_punc; int spk_cur_phonetic; char spk_str_caps_start[MAXVARLEN + 1] = "\0"; @@ -183,13 +183,13 @@ char *spk_default_chars[256] = { /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut" }; -/* array of 256 u_short (one for each character) +/* array of 256 u16 (one for each character) * initialized to default_chartab and user selectable via * /sys/module/speakup/parameters/chartab */ -u_short spk_chartab[256]; +u16 spk_chartab[256]; -static u_short default_chartab[256] = { +static u16 default_chartab[256] = { B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */ B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */ B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */ @@ -267,10 +267,10 @@ static void speakup_date(struct vc_data *vc) spk_y = spk_cy = vc->state.y; spk_pos = spk_cp = vc->vc_pos; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)spk_pos); + spk_attr = get_attributes(vc, (u16 *)spk_pos); } -static void bleep(u_short val) +static void bleep(u16 val) { static const short vals[] = { 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659 @@ -346,14 +346,14 @@ static void speakup_cut(struct vc_data *vc) if (!mark_cut_flag) { mark_cut_flag = 1; - spk_xs = (u_short)spk_x; - spk_ys = (u_short)spk_y; + spk_xs = (u16)spk_x; + spk_ys = (u16)spk_y; spk_sel_cons = vc; synth_printf("%s\n", spk_msg_get(MSG_MARK)); return; } - spk_xe = (u_short)spk_x; - spk_ye = (u_short)spk_y; + spk_xe = (u16)spk_x; + spk_ye = (u16)spk_y; mark_cut_flag = 0; synth_printf("%s\n", spk_msg_get(MSG_CUT)); @@ -482,7 +482,7 @@ static void say_char(struct vc_data *vc) u16 ch; spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)spk_pos, &spk_attr); + ch = get_char(vc, (u16 *)spk_pos, &spk_attr); if (spk_attr != spk_old_attr) { if (spk_attrib_bleep & 1) bleep(spk_y); @@ -497,7 +497,7 @@ static void say_phonetic_char(struct vc_data *vc) u16 ch; spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)spk_pos, &spk_attr); + ch = get_char(vc, (u16 *)spk_pos, &spk_attr); if (ch <= 0x7f && isalpha(ch)) { ch &= 0x1f; synth_printf("%s\n", phonetic[--ch]); @@ -549,7 +549,7 @@ static u_long get_word(struct vc_data *vc) u_char temp; spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)tmp_pos, &temp); + ch = get_char(vc, (u16 *)tmp_pos, &temp); /* decided to take out the sayword if on a space (mis-information */ if (spk_say_word_ctl && ch == SPACE) { @@ -558,26 +558,26 @@ static u_long get_word(struct vc_data *vc) return 0; } else if (tmpx < vc->vc_cols - 2 && (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) && - get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) { + get_char(vc, (u16 *)tmp_pos + 1, &temp) > SPACE) { tmp_pos += 2; tmpx++; } else { while (tmpx > 0) { - ch = get_char(vc, (u_short *)tmp_pos - 1, &temp); + ch = get_char(vc, (u16 *)tmp_pos - 1, &temp); if ((ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) && - get_char(vc, (u_short *)tmp_pos, &temp) > SPACE) + get_char(vc, (u16 *)tmp_pos, &temp) > SPACE) break; tmp_pos -= 2; tmpx--; } } - attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr); + attr_ch = get_char(vc, (u16 *)tmp_pos, &spk_attr); buf[cnt++] = attr_ch; while (tmpx < vc->vc_cols - 1 && cnt < ARRAY_SIZE(buf) - 1) { tmp_pos += 2; tmpx++; - ch = get_char(vc, (u_short *)tmp_pos, &temp); + ch = get_char(vc, (u16 *)tmp_pos, &temp); if (ch == SPACE || ch == 0 || (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) && ch > SPACE)) @@ -591,7 +591,7 @@ static u_long get_word(struct vc_data *vc) static void say_word(struct vc_data *vc) { u_long cnt = get_word(vc); - u_short saved_punc_mask = spk_punc_mask; + u16 saved_punc_mask = spk_punc_mask; if (cnt == 0) return; @@ -606,7 +606,7 @@ static void say_prev_word(struct vc_data *vc) u_char temp; u16 ch; enum edge edge_said = edge_none; - u_short last_state = 0, state = 0; + u16 last_state = 0, state = 0; spk_parked |= 0x01; @@ -635,7 +635,7 @@ static void say_prev_word(struct vc_data *vc) spk_x--; } spk_pos -= 2; - ch = get_char(vc, (u_short *)spk_pos, &temp); + ch = get_char(vc, (u16 *)spk_pos, &temp); if (ch == SPACE || ch == 0) state = 0; else if (ch < 0x100 && IS_WDLM(ch)) @@ -661,7 +661,7 @@ static void say_next_word(struct vc_data *vc) u_char temp; u16 ch; enum edge edge_said = edge_none; - u_short last_state = 2, state = 0; + u16 last_state = 2, state = 0; spk_parked |= 0x01; if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) { @@ -669,7 +669,7 @@ static void say_next_word(struct vc_data *vc) return; } while (1) { - ch = get_char(vc, (u_short *)spk_pos, &temp); + ch = get_char(vc, (u16 *)spk_pos, &temp); if (ch == SPACE || ch == 0) state = 0; else if (ch < 0x100 && IS_WDLM(ch)) @@ -755,9 +755,9 @@ static int get_line(struct vc_data *vc) u_char tmp2; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)spk_pos); + spk_attr = get_attributes(vc, (u16 *)spk_pos); for (i = 0; i < vc->vc_cols; i++) { - buf[i] = get_char(vc, (u_short *)tmp, &tmp2); + buf[i] = get_char(vc, (u16 *)tmp, &tmp2); tmp += 2; } for (--i; i >= 0; i--) @@ -770,7 +770,7 @@ static void say_line(struct vc_data *vc) { int i = get_line(vc); u16 *cp; - u_short saved_punc_mask = spk_punc_mask; + u16 saved_punc_mask = spk_punc_mask; if (i == 0) { synth_printf("%s\n", spk_msg_get(MSG_BLANK)); @@ -817,12 +817,12 @@ static int say_from_to(struct vc_data *vc, u_long from, u_long to, { int i = 0; u_char tmp; - u_short saved_punc_mask = spk_punc_mask; + u16 saved_punc_mask = spk_punc_mask; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)from); + spk_attr = get_attributes(vc, (u16 *)from); while (from < to) { - buf[i++] = get_char(vc, (u_short *)from, &tmp); + buf[i++] = get_char(vc, (u16 *)from, &tmp); from += 2; if (i >= vc->vc_size_row) break; @@ -895,10 +895,10 @@ static int get_sentence_buf(struct vc_data *vc, int read_punc) sentmarks[bn][0] = &sentbuf[bn][0]; i = 0; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)start); + spk_attr = get_attributes(vc, (u16 *)start); while (start < end) { - sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp); + sentbuf[bn][i] = get_char(vc, (u16 *)start, &tmp); if (i > 0) { if (sentbuf[bn][i] == SPACE && sentbuf[bn][i - 1] == '.' && @@ -1047,7 +1047,7 @@ static void say_position(struct vc_data *vc) static void say_char_num(struct vc_data *vc) { u_char tmp; - u16 ch = get_char(vc, (u_short *)spk_pos, &tmp); + u16 ch = get_char(vc, (u16 *)spk_pos, &tmp); synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch); } @@ -1080,7 +1080,7 @@ static void spkup_write(const u16 *in_buf, int count) { static int rep_count; static u16 ch = '\0', old_ch = '\0'; - static u_short char_type, last_type; + static u16 char_type, last_type; int in_count = count; spk_keydown = 0; @@ -1325,9 +1325,9 @@ void spk_reset_default_chartab(void) static const struct st_bits_data *pb_edit; -static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key) +static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u16 key) { - short mask = pb_edit->mask, ch_type = spk_chartab[ch]; + u16 mask = pb_edit->mask, ch_type = spk_chartab[ch]; if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE) return -1; @@ -1947,7 +1947,7 @@ static void speakup_bits(struct vc_data *vc) spk_special_handler = edit_bits; } -static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key) +static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u16 key) { static u_char goto_buf[8]; static int num; @@ -2105,7 +2105,7 @@ static void do_spkup(struct vc_data *vc, u_char value) static const char *pad_chars = "0123456789+-*/\015,.?()"; static int -speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym, +speakup_key(struct vc_data *vc, int shift_state, int keycode, u16 keysym, int up_flag) { unsigned long flags; @@ -2444,6 +2444,7 @@ error_kbdnotifier: mutex_lock(&spk_mutex); synth_release(); mutex_unlock(&spk_mutex); + spk_ttyio_unregister_ldisc(); speakup_kobj_exit(); error_kobjects: diff --git a/drivers/accessibility/speakup/selection.c b/drivers/accessibility/speakup/selection.c index 7df7afad5ab4..1713ce4e0ba5 100644 --- a/drivers/accessibility/speakup/selection.c +++ b/drivers/accessibility/speakup/selection.c @@ -13,7 +13,7 @@ #include "speakup.h" -unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ +u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ struct vc_data *spk_sel_cons; struct speakup_selection_work { diff --git a/drivers/accessibility/speakup/speakup.h b/drivers/accessibility/speakup/speakup.h index 54f1226ea061..984a729fd82d 100644 --- a/drivers/accessibility/speakup/speakup.h +++ b/drivers/accessibility/speakup/speakup.h @@ -62,7 +62,7 @@ int spk_set_num_var(int val, struct st_var_header *var, int how); int spk_set_string_var(const char *page, struct st_var_header *var, int len); int spk_set_mask_bits(const char *input, const int which, const int how); extern special_func spk_special_handler; -int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key); +int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key); int synth_init(char *name); void synth_release(void); @@ -82,7 +82,7 @@ void synth_writeu(const char *buf, size_t count); int synth_supports_indexing(void); extern struct vc_data *spk_sel_cons; -extern unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ +extern u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ extern wait_queue_head_t speakup_event; extern struct kobject *speakup_kobj; @@ -95,20 +95,20 @@ extern struct st_spk_t *speakup_console[]; extern struct spk_synth *synth; extern char spk_pitch_buff[]; extern u_char *spk_our_keys[]; -extern short spk_punc_masks[]; +extern u16 spk_punc_masks[]; extern char spk_str_caps_start[], spk_str_caps_stop[], spk_str_pause[]; extern bool spk_paused; extern const struct st_bits_data spk_punc_info[]; extern u_char spk_key_buf[600]; extern char *spk_characters[]; extern char *spk_default_chars[]; -extern u_short spk_chartab[]; +extern u16 spk_chartab[]; extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level; extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps; extern int spk_bleep_time, spk_bell_pos; extern int spk_spell_delay, spk_key_echo; extern int spk_cur_phonetic; -extern short spk_punc_mask; +extern u16 spk_punc_mask; extern short spk_pitch_shift, synth_flags; extern bool spk_quiet_boot; extern char *synth_name; diff --git a/drivers/accessibility/speakup/speakup_acnt.h b/drivers/accessibility/speakup/speakup_acnt.h index cffa938ae580..cea05d770f6d 100644 --- a/drivers/accessibility/speakup/speakup_acnt.h +++ b/drivers/accessibility/speakup/speakup_acnt.h @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* speakup_acntpc.h - header file for speakups Accent-PC driver. */ +#ifndef _SPEAKUP_ACNT_H +#define _SPEAKUP_ACNT_H #define SYNTH_IO_EXTENT 0x02 @@ -17,3 +19,4 @@ #define SYNTH_FULL 'F' /* synth is full. */ #define SYNTH_ALMOST_EMPTY 'M' /* synth has less than 2 seconds of text left */ #define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */ +#endif diff --git a/drivers/accessibility/speakup/speakup_decpc.c b/drivers/accessibility/speakup/speakup_decpc.c index 083ca9265805..1bf36d1e5477 100644 --- a/drivers/accessibility/speakup/speakup_decpc.c +++ b/drivers/accessibility/speakup/speakup_decpc.c @@ -41,7 +41,7 @@ #define STAT_new_index 0x0040 /* new last index ready */ #define STAT_new_status 0x0080 /* new status posted */ #define STAT_dma_state 0x0100 /* dma state toggle */ -#define STAT_index_valid 0x0200 /* indexs are valid */ +#define STAT_index_valid 0x0200 /* indexes are valid */ #define STAT_flushing 0x0400 /* flush in progress */ #define STAT_self_test 0x0800 /* module in self test */ #define MODE_ready 0xc000 /* module ready for next phase */ diff --git a/drivers/accessibility/speakup/speakup_dtlk.h b/drivers/accessibility/speakup/speakup_dtlk.h index 9c378b58066e..9354e3382bba 100644 --- a/drivers/accessibility/speakup/speakup_dtlk.h +++ b/drivers/accessibility/speakup/speakup_dtlk.h @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* speakup_dtlk.h - header file for speakups DoubleTalk driver. */ +#ifndef _SPEAKUP_DTLK_H +#define _SPEAKUP_DTLK_H #define SYNTH_IO_EXTENT 0x02 #define SYNTH_CLEAR 0x18 /* stops speech */ @@ -39,7 +41,7 @@ /* data returned by Interrogate command */ struct synth_settings { - u_short serial_number; /* 0-7Fh:0-7Fh */ + u16 serial_number; /* 0-7Fh:0-7Fh */ u_char rom_version[24]; /* null terminated string */ u_char mode; /* 0=Character; 1=Phoneme; 2=Text */ u_char punc_level; /* nB; 0-7 */ @@ -61,3 +63,4 @@ struct synth_settings { */ u_char has_indexing; /* nonzero if indexing is implemented */ }; +#endif diff --git a/drivers/accessibility/speakup/speakup_soft.c b/drivers/accessibility/speakup/speakup_soft.c index 6d446824677b..57d83b82f1d1 100644 --- a/drivers/accessibility/speakup/speakup_soft.c +++ b/drivers/accessibility/speakup/speakup_soft.c @@ -446,8 +446,9 @@ static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var) if (var->var_id != PUNC_LEVEL) return 0; - /* We want to set the the speech synthesis punctuation level - * accordingly, so it properly tunes speaking A_PUNC characters */ + /* We want to set the speech synthesis punctuation level + * accordingly, so it properly tunes speaking A_PUNC characters + */ var_data = var->data; if (!var_data) return 0; diff --git a/drivers/accessibility/speakup/spk_types.h b/drivers/accessibility/speakup/spk_types.h index 08011518a28a..a5762330e249 100644 --- a/drivers/accessibility/speakup/spk_types.h +++ b/drivers/accessibility/speakup/spk_types.h @@ -53,7 +53,7 @@ enum var_id_t { }; typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch, - u_short key); + u16 key); #define COLOR_BUFFER_SIZE 160 diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c index d8addbf3ad0d..11cba1fd8715 100644 --- a/drivers/accessibility/speakup/synth.c +++ b/drivers/accessibility/speakup/synth.c @@ -163,7 +163,7 @@ int spk_synth_is_alive_restart(struct spk_synth *synth) /* restart */ synth->alive = 1; synth_printf("%s", synth->init); - return 2; /* reenabled */ + return 2; /* re-enabled */ } pr_warn("%s: can't restart synth\n", synth->long_name); return 0; @@ -574,4 +574,4 @@ struct spk_synth *synth_current(void) } EXPORT_SYMBOL_GPL(synth_current); -short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; +u16 spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; diff --git a/drivers/accessibility/speakup/utils.h b/drivers/accessibility/speakup/utils.h index 4ce9a12f7664..db00c962f8e2 100644 --- a/drivers/accessibility/speakup/utils.h +++ b/drivers/accessibility/speakup/utils.h @@ -36,7 +36,7 @@ static inline void open_input(const char *dir_name, const char *name) else snprintf(filename, sizeof(filename), "%s", name); infile = fopen(filename, "r"); - if (infile == 0) { + if (!infile) { fprintf(stderr, "can't open %s\n", filename); exit(1); } diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index e2e402c9d175..606a9d07f774 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC config ANDROID_BINDER_IPC_RUST bool "Rust version of Android Binder IPC Driver" - depends on RUST && MMU && !ANDROID_BINDER_IPC + depends on RUST && MMU && NET && !ANDROID_BINDER_IPC help This enables the Rust implementation of the Binder driver. diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index ea5846e4da16..165cb797eb1e 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -384,8 +384,8 @@ impl<'a> AllocationView<'a> { BINDER_TYPE_WEAK_BINDER }; newobj.flags = obj.flags; - newobj.__bindgen_anon_1.binder = ptr as _; - newobj.cookie = cookie as _; + newobj.__bindgen_anon_1.binder = ptr as uapi::binder_uintptr_t; + newobj.cookie = cookie as uapi::binder_uintptr_t; self.write(offset, &newobj)?; // Increment the user ref count on the node. It will be decremented as part of the // destruction of the buffer, when we see a binder or weak-binder object. diff --git a/drivers/android/binder/debug.rs b/drivers/android/binder/debug.rs new file mode 100644 index 000000000000..824b10c004c3 --- /dev/null +++ b/drivers/android/binder/debug.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Google LLC. + +//! Binder debugging helpers. + +#![allow(dead_code)] + +use kernel::bits::bit_u32; +use kernel::sync::atomic::Atomic; + +kernel::impl_flags!( + /// Represents multiple debug mask flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct DebugMasks(u32); + + /// Represents a single debug mask category. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum DebugMask { + UserError = bit_u32(0), + FailedTransaction = bit_u32(1), + DeadTransaction = bit_u32(2), + OpenClose = bit_u32(3), + DeadBinder = bit_u32(4), + DeathNotification = bit_u32(5), + ReadWrite = bit_u32(6), + UserRefs = bit_u32(7), + Threads = bit_u32(8), + Transaction = bit_u32(9), + TransactionComplete = bit_u32(10), + FreeBuffer = bit_u32(11), + InternalRefs = bit_u32(12), + PriorityCap = bit_u32(13), + Spinlocks = bit_u32(14), + } +); + +#[no_mangle] +pub(crate) static rust_binder_debug_mask: Atomic<u32> = Atomic::new( + (DebugMask::UserError as u32) + | (DebugMask::FailedTransaction as u32) + | (DebugMask::DeadTransaction as u32), +); + +/// Checks if the given debug logging category is enabled in the mask. +pub(crate) fn debug_mask_enabled(mask: DebugMask) -> bool { + let current_mask = rust_binder_debug_mask.load(kernel::sync::atomic::Relaxed); + DebugMasks(current_mask).contains(mask) +} + +/// Prints a debug log if the specified mask category is enabled. +#[macro_export] +macro_rules! binder_debug { + // Rule to explicitly specify a PID (used in kworkers). + (pid=$pid:expr, $mask:ident, $($arg:tt)*) => { + if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { + kernel::pr_info!( + "{}: {}\n", + $pid, + kernel::prelude::fmt!($($arg)*) + ); + } + }; + + // Default rule (automatically prepends "PID:TID" of the current calling thread). + ($mask:ident, $($arg:tt)*) => { + if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { + let thread = kernel::current!(); + kernel::pr_info!( + "{}:{} {}\n", + thread.tgid(), + thread.pid(), + kernel::prelude::fmt!($($arg)*) + ); + } + }; +} diff --git a/drivers/android/binder/defs.rs b/drivers/android/binder/defs.rs index 33f51b4139c7..8ac9bdd7a499 100644 --- a/drivers/android/binder/defs.rs +++ b/drivers/android/binder/defs.rs @@ -4,6 +4,8 @@ use core::mem::MaybeUninit; use core::ops::{Deref, DerefMut}; +use core::ptr; + use kernel::{ transmute::{AsBytes, FromBytes}, uapi::{self, *}, @@ -146,7 +148,7 @@ decl_wrapper!(ExtendedError, uapi::binder_extended_error); impl BinderVersion { pub(crate) fn current() -> Self { Self(MaybeUninit::new(uapi::binder_version { - protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as _, + protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as i32, })) } } @@ -165,8 +167,8 @@ impl BinderTransactionDataSecctx { pub(crate) fn tr_data(&mut self) -> &mut BinderTransactionData { // SAFETY: Transparent wrapper is safe to transmute. unsafe { - &mut *(&mut self.transaction_data as *mut uapi::binder_transaction_data - as *mut BinderTransactionData) + &mut *(ptr::from_mut::<uapi::binder_transaction_data>(&mut self.transaction_data) + .cast::<BinderTransactionData>()) } } } diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index f4df14568b25..66912b4cb527 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -60,6 +60,7 @@ type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>; /// Represents a notification that the freeze state has changed. pub(crate) struct FreezeMessage { cookie: FreezeCookie, + pid: i32, } kernel::list::impl_list_arc_safe! { @@ -73,8 +74,8 @@ impl FreezeMessage { UniqueArc::new_uninit(flags) } - fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc<FreezeMessage> { - match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) { + fn init(ua: UninitFM, cookie: FreezeCookie, pid: i32) -> DLArc<FreezeMessage> { + match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie, pid })) { Ok(msg) => ListArc::from(msg), Err(err) => match err {}, } @@ -127,7 +128,7 @@ impl DeliverToRead for FreezeMessage { } let mut state_info = BinderFrozenStateInfo::default(); - state_info.is_frozen = is_frozen as u32; + state_info.is_frozen = u32::from(is_frozen); state_info.cookie = freeze.cookie.0; freeze.is_pending = true; freeze.last_is_frozen = Some(is_frozen); @@ -140,7 +141,14 @@ impl DeliverToRead for FreezeMessage { } } - fn cancel(self: DArc<Self>) {} + fn cancel(self: DArc<Self>) { + binder_debug!( + pid = self.pid, + DeadTransaction, + "undelivered freeze notification, {:016x}", + self.cookie.0 + ); + } fn should_sync_wakeup(&self) -> bool { false @@ -180,36 +188,61 @@ impl Process { let msg = FreezeMessage::new(GFP_KERNEL)?; let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?; + let mut afl_vec_alloc = KVVec::new(); + let mut info; + let mut freeze_entry; let mut node_refs_guard = self.node_refs.lock(); - let node_refs = &mut *node_refs_guard; - let Some(info) = node_refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle); - return Err(EINVAL); - }; - if info.freeze().is_some() { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n"); - return Err(EINVAL); - } - let node_ref = info.node_ref(); - let freeze_entry = node_refs.freeze_listeners.entry(cookie); - - if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { - if !dupe.get().allow_duplicate(&node_ref.node) { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n"); + loop { + let node_refs = &mut *node_refs_guard; + info = match node_refs.by_handle.get_mut(&handle) { + Some(info) => info, + None => { + binder_debug!( + UserError, + "BC_REQUEST_FREEZE_NOTIFICATION invalid ref {handle}" + ); + return Err(EINVAL); + } + }; + if info.freeze().is_some() { + binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION already set"); return Err(EINVAL); } - } + let node_ref = info.node_ref(); + freeze_entry = node_refs.freeze_listeners.entry(cookie); + + if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { + if !dupe.get().allow_duplicate(&node_ref.node) { + binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie"); + return Err(EINVAL); + } + } - // All failure paths must come before this call, and all modifications must come after this - // call. - node_ref.node.add_freeze_listener(self, GFP_KERNEL)?; + // Now we add to the node's freeze listener list, with retry and re-allocate if the + // vector is full. + // + // To ensure that the node is added atomically, this is the first time we modify any + // state. When this call succeeds, all other modifications must occur without the + // possibility for any failure paths. + match node_ref + .node + .add_freeze_listener(self, &mut afl_vec_alloc)? + { + Ok(()) => break, + Err(resize_target) => { + drop(node_refs_guard); + afl_vec_alloc = KVVec::with_capacity(resize_target, GFP_KERNEL)?; + node_refs_guard = self.node_refs.lock(); + } + } + } match freeze_entry { rbtree::Entry::Vacant(entry) => { entry.insert( FreezeListener { cookie, - node: node_ref.node.clone(), + node: info.node_ref().node.clone(), last_is_frozen: None, is_pending: false, is_clearing: false, @@ -233,7 +266,7 @@ impl Process { } *info.freeze() = Some(cookie); - let msg = FreezeMessage::init(msg, cookie); + let msg = FreezeMessage::init(msg, cookie, self.task.pid()); drop(node_refs_guard); let _ = self.push_work(msg); Ok(()) @@ -245,18 +278,23 @@ impl Process { let mut node_refs_guard = self.node_refs.lock(); let node_refs = &mut *node_refs_guard; let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else { - pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0); + binder_debug!( + UserError, + "BC_FREEZE_NOTIFICATION_DONE {:016x} not found", + cookie.0 + ); return Err(EINVAL); }; let mut clear_msg = None; if freeze.num_pending_duplicates > 0 { - clear_msg = Some(FreezeMessage::init(alloc, cookie)); + clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); freeze.num_pending_duplicates -= 1; freeze.num_cleared_duplicates += 1; } else { if !freeze.is_pending { - pr_warn!( - "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n", + binder_debug!( + UserError, + "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending", cookie.0 ); return Err(EINVAL); @@ -264,7 +302,7 @@ impl Process { let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen(); if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) { // Immediately send another FreezeMessage. - clear_msg = Some(FreezeMessage::init(alloc, cookie)); + clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); } freeze.is_pending = false; } @@ -280,31 +318,44 @@ impl Process { let handle = hc.handle; let cookie = FreezeCookie(hc.cookie); + let _to_free_fl; let alloc = FreezeMessage::new(GFP_KERNEL)?; let mut node_refs_guard = self.node_refs.lock(); let node_refs = &mut *node_refs_guard; let Some(info) = node_refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION invalid ref {handle}" + ); return Err(EINVAL); }; let Some(info_cookie) = info.freeze() else { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n"); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active" + ); return Err(EINVAL); }; if *info_cookie != cookie { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n"); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch" + ); return Err(EINVAL); } let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {handle}" + ); return Err(EINVAL); }; listener.is_clearing = true; - listener.node.remove_freeze_listener(self); + _to_free_fl = listener.node.remove_freeze_listener(self); *info.freeze() = None; let mut msg = None; if !listener.is_pending { - msg = Some(FreezeMessage::init(alloc, cookie)); + msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); } drop(node_refs_guard); @@ -384,7 +435,7 @@ impl Process { continue; }; let msg_alloc = FreezeMessage::new(GFP_KERNEL)?; - let msg = FreezeMessage::init(msg_alloc, cookie); + let msg = FreezeMessage::init(msg_alloc, cookie, proc.task.pid()); batch.push((proc, msg), GFP_KERNEL)?; } diff --git a/drivers/android/binder/netlink.rs b/drivers/android/binder/netlink.rs new file mode 100644 index 000000000000..f34e1009432c --- /dev/null +++ b/drivers/android/binder/netlink.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +/* Based on: Documentation/netlink/specs/binder.yaml */ + +#![allow(unreachable_pub, clippy::wrong_self_convention)] +use kernel::{ + net::netlink::{ + Family, + GenlMsg, + MulticastGroup, + NetlinkSkBuff, // + }, + prelude::*, // +}; + +pub static BINDER_NL_FAMILY: Family = Family::const_new( + kernel::module::this_module::<crate::LocalModule>(), + kernel::uapi::BINDER_FAMILY_NAME, + kernel::uapi::BINDER_FAMILY_VERSION, + &BINDER_NL_FAMILY_MCGRPS, +); + +static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] = [MulticastGroup::const_new(c"report")]; + +/// A multicast event sent to userspace subscribers to notify them about +/// binder transaction failures. The generated report provides the full +/// details of the specific transaction that failed. The intention is for +/// programs to monitor these events and react to the failures as needed. +pub struct Report { + skb: GenlMsg, +} + +impl Report { + /// Create a new multicast message. + pub fn new( + size: usize, + portid: u32, + seq: u32, + flags: kernel::alloc::Flags, + ) -> Result<Self, kernel::alloc::AllocError> { + const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8; + let skb = NetlinkSkBuff::new(size, flags)?; + let skb = skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER_CMD_REPORT)?; + Ok(Self { skb }) + } + + /// Broadcast this message. + pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Result { + self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags) + } + + /// Check if this message type has listeners. + pub fn has_listeners() -> bool { + BINDER_NL_FAMILY.has_listeners(0) + } + + /// The enum binder_driver_return_protocol returned to the sender. + pub fn error(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int; + self.skb.put_u32(BINDER_A_REPORT_ERROR, val) + } + + /// The binder context where the transaction occurred. + pub fn context(&mut self, val: &CStr) -> Result { + const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int; + self.skb.put_string(BINDER_A_REPORT_CONTEXT, val) + } + + /// The PID of the sender process. + pub fn from_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val) + } + + /// The TID of the sender thread. + pub fn from_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val) + } + + /// The PID of the recipient process. This attribute may not be present + /// if the target could not be determined. + pub fn to_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_PID, val) + } + + /// The TID of the recipient thread. This attribute may not be present + /// if the target could not be determined. + pub fn to_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_TID, val) + } + + /// When present, indicates the failed transaction is a reply. + pub fn is_reply(&mut self) -> Result { + const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int; + self.skb.put_flag(BINDER_A_REPORT_IS_REPLY) + } + + /// The bitmask of enum transaction_flags from the transaction. + pub fn flags(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int; + self.skb.put_u32(BINDER_A_REPORT_FLAGS, val) + } + + /// The application-defined code from the transaction. + pub fn code(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int; + self.skb.put_u32(BINDER_A_REPORT_CODE, val) + } + + /// The transaction payload size in bytes. + pub fn data_size(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int; + self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val) + } +} diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index c10148e9069f..0a82af14cda3 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -9,6 +9,7 @@ use kernel::{ seq_print, sync::lock::{spinlock::SpinLockBackend, Guard}, sync::{Arc, LockedBy, SpinLock}, + uapi, }; use crate::{ @@ -21,6 +22,7 @@ use crate::{ }; use core::mem; +use core::ptr; mod wrapper; pub(crate) use self::wrapper::CritIncrWrapper; @@ -321,7 +323,7 @@ impl Node { /// An id that is unique across all binder nodes on the system. Used as the key in the /// `by_node` map. pub(crate) fn global_id(&self) -> usize { - self as *const Node as usize + ptr::from_ref(self).addr() } pub(crate) fn get_id(&self) -> (u64, u64) { @@ -333,6 +335,10 @@ impl Node { death: ListArc<DTRWrap<NodeDeath>, 1>, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>, ) { + assert!( + core::ptr::eq(self, &**death.node), + "attempt to add NodeDeath to the wrong death list" + ); self.inner.access_mut(guard).death_list.push_back(death); } @@ -343,7 +349,7 @@ impl Node { ) -> Option<DLArc<Node>> { let inner = self.inner.access_mut(owner_inner); if inner.active_inc_refs == 0 { - pr_err!("inc_ref_done called when no active inc_refs"); + binder_debug!(UserError, "inc_ref_done called when no active inc_refs"); return None; } @@ -464,7 +470,7 @@ impl Node { owner_inner: &mut ProcessInner, ) -> Option<DLArc<dyn DeliverToRead>> { match self.incr_refcount_allow_zero2one(strong, owner_inner) { - Ok(Some(node)) => Some(node as _), + Ok(Some(node)) => Some(node as DLArc<dyn DeliverToRead>), Ok(None) => None, Err(CouldNotDeliverCriticalIncrement) => { assert!(strong); @@ -489,8 +495,8 @@ impl Node { guard: &Guard<'_, ProcessInner, SpinLockBackend>, ) { let inner = self.inner.access(guard); - out.strong_count = inner.strong.count as _; - out.weak_count = inner.weak.count as _; + out.strong_count = inner.strong.count as u32; + out.weak_count = inner.weak.count as u32; } pub(crate) fn populate_debug_info( @@ -498,8 +504,8 @@ impl Node { out: &mut BinderNodeDebugInfo, guard: &Guard<'_, ProcessInner, SpinLockBackend>, ) { - out.ptr = self.ptr as _; - out.cookie = self.cookie as _; + out.ptr = self.ptr as uapi::binder_uintptr_t; + out.cookie = self.cookie as uapi::binder_uintptr_t; let inner = self.inner.access(guard); if inner.strong.has_count { out.has_strong_ref = 1; @@ -536,7 +542,7 @@ impl Node { inner.oneway_todo.push_back(transaction); } else { inner.has_oneway_transaction = true; - guard.push_work(transaction)?; + guard.push_work(&self.owner, transaction)?; } Ok(()) } @@ -568,7 +574,7 @@ impl Node { let transaction = inner.oneway_todo.pop_front(); inner.has_oneway_transaction = transaction.is_some(); if let Some(transaction) = transaction { - match guard.push_work(transaction) { + match guard.push_work(&self.owner, transaction) { Ok(()) => {} Err((_err, work)) => { // Process is dead. @@ -657,29 +663,26 @@ impl Node { pub(crate) fn add_freeze_listener( &self, process: &Arc<Process>, - flags: kernel::alloc::Flags, - ) -> Result { - let mut vec_alloc = KVVec::<Arc<Process>>::new(); - loop { - let mut guard = self.owner.inner.lock(); - // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the - // listener, no the target. - let inner = self.inner.access_mut(&mut guard); - let len = inner.freeze_list.len(); - if len >= inner.freeze_list.capacity() { - if len >= vec_alloc.capacity() { - drop(guard); - vec_alloc = KVVec::with_capacity((1 + len).next_power_of_two(), flags)?; - continue; - } - mem::swap(&mut inner.freeze_list, &mut vec_alloc); - for elem in vec_alloc.drain_all() { - inner.freeze_list.push_within_capacity(elem)?; - } + // If the vector needs to be resized, it's done via this argument. + vec_alloc: &mut KVVec<Arc<Process>>, + ) -> Result<Result<(), usize>> { + let mut guard = self.owner.inner.lock(); + // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the + // listener, not the target. + let inner = self.inner.access_mut(&mut guard); + let len = inner.freeze_list.len(); + if len == inner.freeze_list.capacity() { + if len >= vec_alloc.capacity() { + // Request the caller to reallocate. + return Ok(Err((1 + len).next_power_of_two())); + } + mem::swap(&mut inner.freeze_list, vec_alloc); + for elem in vec_alloc.drain_all() { + inner.freeze_list.push_within_capacity(elem)?; } - inner.freeze_list.push_within_capacity(process.clone())?; - return Ok(()); } + inner.freeze_list.push_within_capacity(process.clone())?; + Ok(Ok(())) } pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec<Arc<Process>> { @@ -695,6 +698,8 @@ impl Node { p.pid_in_current_ns() ); } + // If the vector is empty it needs to be freed. However, we can't free it here because that + // might sleep, so return it to the caller. if inner.freeze_list.is_empty() { return mem::take(&mut inner.freeze_list); } @@ -820,6 +825,7 @@ impl NodeRef { pub(crate) fn clone(&self, strong: bool) -> Result<NodeRef> { if strong && self.strong_count == 0 { + binder_debug!(UserError, "tried to use weak ref as strong ref"); return Err(EINVAL); } Ok(self @@ -860,9 +866,10 @@ impl NodeRef { *count += 1; } else { if *count == 0 { - pr_warn!( - "pid {} performed invalid decrement on ref\n", - kernel::current!().pid() + binder_debug!( + UserError, + "performed invalid {} decrement on ref", + if strong { "strong" } else { "weak" } ); return false; } @@ -1104,6 +1111,11 @@ impl DeliverToRead for NodeDeath { // We're still holding the inner lock, so it cannot be aborted while we insert it into // the delivered list. process_inner.death_delivered(self.clone()); + binder_debug!( + DeathNotification, + "sending death notification, cookie {:016x}", + cookie + ); BR_DEAD_BINDER }; @@ -1114,7 +1126,14 @@ impl DeliverToRead for NodeDeath { Ok(cmd != BR_DEAD_BINDER) } - fn cancel(self: DArc<Self>) {} + fn cancel(self: DArc<Self>) { + binder_debug!( + pid = self.process.task.pid(), + DeadTransaction, + "undelivered death notification, {:016x}", + self.cookie + ); + } fn should_sync_wakeup(&self) -> bool { false diff --git a/drivers/android/binder/node/wrapper.rs b/drivers/android/binder/node/wrapper.rs index 43294c050502..6e4ca01c941a 100644 --- a/drivers/android/binder/node/wrapper.rs +++ b/drivers/android/binder/node/wrapper.rs @@ -21,7 +21,7 @@ impl CritIncrWrapper { pub(super) fn init(self, node: DArc<Node>) -> DLArc<dyn DeliverToRead> { match self.inner.pin_init_with(DTRWrap::new(NodeWrapper { node })) { - Ok(initialized) => ListArc::from(initialized) as _, + Ok(initialized) => ListArc::from(initialized) as DLArc<dyn DeliverToRead>, Err(err) => match err {}, } } diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs index e82a5523804f..52ffbf3504e7 100644 --- a/drivers/android/binder/page_range.rs +++ b/drivers/android/binder/page_range.rs @@ -312,7 +312,7 @@ impl ShrinkablePageRange { // SAFETY: This just initializes the pages array. unsafe { - let self_ptr = self as *const ShrinkablePageRange; + let self_ptr = ptr::from_ref(self); for i in 0..num_pages { let info = pages.as_mut_ptr().add(i); (&raw mut (*info).range).write(self_ptr); @@ -571,7 +571,7 @@ impl ShrinkablePageRange { unsafe { self.iterate(offset, size_of::<T>(), |page, offset, to_copy| { // SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T. - let obj_ptr = (out.as_mut_ptr() as *mut u8).add(out_offset); + let obj_ptr = out.as_mut_ptr().cast::<u8>().add(out_offset); // SAFETY: The pointer points is in-bounds of the `out` variable, so it is valid. page.read_raw(obj_ptr, offset, to_copy)?; out_offset += to_copy; @@ -593,7 +593,7 @@ impl ShrinkablePageRange { unsafe { self.iterate(offset, size_of_val(obj), |page, offset, to_copy| { // SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T. - let obj_ptr = (obj as *const T as *const u8).add(obj_offset); + let obj_ptr = ptr::from_ref(obj).cast::<u8>().add(obj_offset); // SAFETY: We have a reference to the object, so the pointer is valid. page.write_raw(obj_ptr, offset, to_copy)?; obj_offset += to_copy; @@ -712,7 +712,7 @@ unsafe extern "C" fn rust_shrink_free_page( { // CAST: The `list_head` field is first in `PageInfo`. - let info = item as *mut PageInfo; + let info = item.cast::<PageInfo>(); // SAFETY: The `range` field of `PageInfo` is immutable. range_ptr = unsafe { (*info).range }; // SAFETY: The `range` outlives its `PageInfo` values. diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 5b8f73ec1931..5372bfbd93b3 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -16,6 +16,7 @@ use core::mem::take; use kernel::{ bindings, + bits::bit_u8, cred::Credential, error::Error, fs::file::{self, File}, @@ -30,9 +31,10 @@ use kernel::{ sync::{ aref::ARef, lock::{spinlock::SpinLockBackend, Guard}, - Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc, + poll::PollCondVarBox, + Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SetOnce, SpinLock, UniqueArc, }, - task::Task, + task::{Pid, Task}, uaccess::{UserSlice, UserSliceReader}, uapi, workqueue::{self, Work}, @@ -70,9 +72,18 @@ impl Mapping { } } -// bitflags for defer_work. -const PROC_DEFER_FLUSH: u8 = 1; -const PROC_DEFER_RELEASE: u8 = 2; +kernel::impl_flags!( + /// Represents multiple deferred work flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct DeferWorks(u8); + + /// Represents a single deferred work category. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum DeferWork { + Flush = bit_u8(0), + Release = bit_u8(1), + } +); #[derive(Copy, Clone)] pub(crate) enum IsFrozen { @@ -121,7 +132,7 @@ pub(crate) struct ProcessInner { started_thread_count: u32, /// Bitmap of deferred work to do. - defer_work: u8, + defer_work: DeferWorks, /// Number of transactions to be transmitted before processes in freeze_wait /// are woken up. @@ -151,7 +162,7 @@ impl ProcessInner { requested_thread_count: 0, max_threads: 0, started_thread_count: 0, - defer_work: 0, + defer_work: DeferWorks::default(), outstanding_txns: 0, is_frozen: IsFrozen::No, sync_recv: false, @@ -172,21 +183,26 @@ impl ProcessInner { /// taken while holding the inner process lock. pub(crate) fn push_work( &mut self, + proc: &Process, work: DLArc<dyn DeliverToRead>, ) -> Result<(), (BinderError, DLArc<dyn DeliverToRead>)> { + let sync = work.should_sync_wakeup(); + // Try to find a ready thread to which to push the work. if let Some(thread) = self.ready_threads.pop_front() { // Push to thread while holding state lock. This prevents the thread from giving up // (for example, because of a signal) when we're about to deliver work. - match thread.push_work(work) { + match thread.push_work_inner(work, sync) { PushWorkRes::Ok => Ok(()), + PushWorkRes::OkNotifyPoll => { + proc.notify_poll(sync); + Ok(()) + } PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)), } } else if self.is_dead { Err((BinderError::new_dead(), work)) } else { - let sync = work.should_sync_wakeup(); - // Didn't find a thread waiting for proc work; this can happen // in two scenarios: // 1. All threads are busy handling transactions @@ -194,17 +210,12 @@ impl ProcessInner { // the kernel driver soon and pick up this work. // 2. Threads are using the (e)poll interface, in which case // they may be blocked on the waitqueue without having been - // added to waiting_threads. For this case, we just iterate - // over all threads not handling transaction work, and - // wake them all up. We wake all because we don't know whether - // a thread that called into (e)poll is handling non-binder - // work currently. + // added to waiting_threads. For this case, we wake it up + // directly. self.work.push_back(work); // Wake up polling threads, if any. - for thread in self.threads.values() { - thread.notify_if_poll_ready(sync); - } + proc.notify_poll(sync); Ok(()) } @@ -227,11 +238,11 @@ impl ProcessInner { // If we decided that we need to push work, push either to the process or to a thread if // one is specified. - if let Some(node) = push { + if let Some(pnode) = push { if let Some(thread) = othread { - thread.push_work_deferred(node); + thread.push_work_deferred(pnode); } else { - let _ = self.push_work(node); + let _ = self.push_work(&node.owner, pnode); // Nothing to do: `push_work` may fail if the process is dead, but that's ok as in // that case, it doesn't care about the notification. } @@ -259,7 +270,7 @@ impl ProcessInner { let push = match wrapper { None => node .incr_refcount_allow_zero2one(strong, self)? - .map(|node| node as _), + .map(|node| node as DLArc<dyn DeliverToRead>), Some(wrapper) => node.incr_refcount_allow_zero2one_with_wrapper(strong, wrapper, self), }; if let Some(node) = push { @@ -455,7 +466,13 @@ pub(crate) struct Process { // Node references are in a different lock to avoid recursive acquisition when // incrementing/decrementing a node in another process. #[pin] - node_refs: Mutex<ProcessNodeRefs>, + node_refs: SpinLock<ProcessNodeRefs>, + + // Synchronizes `register_wait` calls to the `PollCondVarBox`. + // + // The `PollCondVarBox` is not stored here because synchronization is + // done for `register_wait` only. Wakeups do not take this lock. + poll: SetOnce<PollCondVarBox>, // Work node for deferred work item. #[pin] @@ -489,13 +506,13 @@ impl workqueue::WorkItem for Process { { let mut inner = me.inner.lock(); defer = inner.defer_work; - inner.defer_work = 0; + inner.defer_work = DeferWorks::default(); } - if defer & PROC_DEFER_FLUSH != 0 { + if defer.contains(DeferWork::Flush) { me.deferred_flush(); } - if defer & PROC_DEFER_RELEASE != 0 { + if defer.contains(DeferWork::Release) { me.deferred_release(); } } @@ -510,12 +527,13 @@ impl Process { cred, inner <- kernel::new_spinlock!(ProcessInner::new(), "Process::inner"), pages <- ShrinkablePageRange::new(&super::BINDER_SHRINKER), - node_refs <- kernel::new_mutex!(ProcessNodeRefs::new(), "Process::node_refs"), + node_refs <- kernel::new_spinlock!(ProcessNodeRefs::new(), "Process::node_refs"), freeze_wait <- kernel::new_condvar!("Process::freeze_wait"), task: current.group_leader().into(), defer_work <- kernel::new_work!("Process::defer_work"), links <- ListLinks::new(), stats: BinderStats::new(), + poll: SetOnce::new(), }), GFP_KERNEL, )?; @@ -715,7 +733,7 @@ impl Process { pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult { // If push_work fails, drop the work item outside the lock. - let res = self.inner.lock().push_work(work); + let res = self.inner.lock().push_work(self, work); match res { Ok(()) => Ok(()), Err((err, work)) => { @@ -741,7 +759,7 @@ impl Process { } else { (0, 0, 0) }; - let node_ref = self.get_node(ptr, cookie, flags as _, true, thread)?; + let node_ref = self.get_node(ptr, cookie, flags, true, thread)?; let node = node_ref.node.clone(); self.ctx.set_manager_node(node_ref)?; self.inner.lock().is_manager = true; @@ -861,14 +879,17 @@ impl Process { let handle = unused_id.as_u32(); // Do a lookup again as node may have been inserted before the lock was reacquired. - if let Some(handle_ref) = refs.by_node.get(&node_ref.node.global_id()) { - let handle = *handle_ref; - let info = refs.by_handle.get_mut(&handle).unwrap(); - info.node_ref().absorb(node_ref); - return Ok(handle); - } + let by_node_slot = match refs.by_node.entry(node_ref.node.global_id()) { + rbtree::Entry::Vacant(by_node_slot) => by_node_slot, + rbtree::Entry::Occupied(handle_ref) => { + // The node was inserted by another thread while we didn't hold the lock. + let handle = handle_ref.get(); + let info = refs.by_handle.get_mut(handle).unwrap(); + info.node_ref().absorb(node_ref); + return Ok(*handle); + } + }; - let gid = node_ref.node.global_id(); let (info_proc, info_node) = { let info_init = NodeRefInfo::new(node_ref, handle, self.into()); match info.pin_init_with(info_init) { @@ -884,6 +905,9 @@ impl Process { // first thing in `deferred_release`, process cleanup will not miss the items inserted into // `refs` below. if self.inner.lock().is_dead { + // Explicitly drop the lock so that `info_proc` and `info_node` are dropped outside of + // the lock. + drop(refs_lock); return Err(ESRCH); } @@ -891,7 +915,7 @@ impl Process { // `info_node` into the right node's `refs` list. unsafe { info_proc.node_ref2().node.insert_node_info(info_node) }; - refs.by_node.insert(reserve1.into_node(gid, handle)); + by_node_slot.insert(handle, reserve1); by_handle_slot.insert(info_proc, reserve2); unused_id.acquire(); Ok(handle) @@ -906,7 +930,13 @@ impl Process { } Ok(node_ref) } else { - Ok(self.get_node_from_handle(handle, true)?) + match self.get_node_from_handle(handle, true) { + Ok(node_ref) => Ok(node_ref), + Err(err) => { + binder_debug!(UserError, "got transaction to invalid handle {handle}"); + Err(err.into()) + } + } } } @@ -946,15 +976,19 @@ impl Process { // To preserve original binder behaviour, we only fail requests where the manager tries to // increment references on itself. + let _to_free_by_handle; + let _to_free_by_node; let _to_free_freeze_listener; let _to_free_freeze_listener_cleanup; let mut refs = self.node_refs.lock(); if let Some(info) = refs.by_handle.get_mut(&handle) { if info.node_ref().update(inc, strong) { // Clean up death if there is one attached to this node reference. - if let Some(death) = info.death().take() { + // + // We remove the entire `info` below, so no need to remove `death` from `info`. + if let Some(death) = info.death().as_ref() { death.set_cleared(true); - self.remove_from_delivered_deaths(&death); + self.remove_from_delivered_deaths(death); } // Remove reference from process tables, and from the node's `refs` list. @@ -971,8 +1005,8 @@ impl Process { } } - refs.by_handle.remove(&handle); - refs.by_node.remove(&id); + _to_free_by_handle = refs.by_handle.remove_node(&handle); + _to_free_by_node = refs.by_node.remove_node(&id); refs.handle_is_present.release_id(handle as usize); if let Some(shrink) = refs.handle_is_present.shrink_request() { @@ -987,7 +1021,7 @@ impl Process { } else { // All refs are cleared in process exit, so this warning is expected in that case. if !self.inner.lock().is_dead { - pr_warn!("{}: no such ref {handle}\n", self.pid_in_current_ns()); + binder_debug!(UserError, "no such ref {handle}"); } } Ok(()) @@ -1008,7 +1042,7 @@ impl Process { if let Ok(Some(node)) = inner.get_existing_node(ptr, cookie) { if let Some(node) = node.inc_ref_done_locked(strong, &mut inner) { // This only fails if the process is dead. - let _ = inner.push_work(node); + let _ = inner.push_work(self, node); } } Ok(()) @@ -1237,16 +1271,26 @@ impl Process { // Queue BR_ERROR if we can't allocate memory for the death notification. let death = UniqueArc::new_uninit(GFP_KERNEL).inspect_err(|_| { thread.push_return_work(BR_ERROR); + binder_debug!( + DeathNotification, + "BC_REQUEST_DEATH_NOTIFICATION failed due to memory allocation failure" + ); })?; let mut refs = self.node_refs.lock(); let Some(info) = refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}\n"); + binder_debug!( + UserError, + "BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}" + ); return Ok(()); }; // Nothing to do if there is already a death notification request for this handle. if info.death().is_some() { - pr_warn!("BC_REQUEST_DEATH_NOTIFICATION death notification already set\n"); + binder_debug!( + UserError, + "BC_REQUEST_DEATH_NOTIFICATION death notification already set" + ); return Ok(()); } @@ -1274,6 +1318,11 @@ impl Process { info.node_ref().node.add_death(death, &mut owner_inner); } } + binder_debug!( + DeathNotification, + "BC_REQUEST_DEATH_NOTIFICATION handle {handle} cookie {:016x}", + cookie + ); Ok(()) } @@ -1283,28 +1332,45 @@ impl Process { let mut refs = self.node_refs.lock(); let Some(info) = refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}\n"); + binder_debug!( + UserError, + "BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}" + ); return Ok(()); }; let Some(death) = info.death().take() else { - pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification not active\n"); + binder_debug!( + UserError, + "BC_CLEAR_DEATH_NOTIFICATION death notification not active" + ); return Ok(()); }; if death.cookie != cookie { *info.death() = Some(death); - pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch\n"); + binder_debug!( + UserError, + "BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch" + ); return Ok(()); } // Update state and determine if we need to queue a work item. We only need to do it when // the node is not dead or if the user already completed the death notification. - if death.set_cleared(false) { + let should_schedule = death.set_cleared(false); + drop(refs); + + if should_schedule { if let Some(death) = ListArc::try_from_arc_or_drop(death) { let _ = thread.push_work_if_looper(death); } } + binder_debug!( + DeathNotification, + "BC_CLEAR_DEATH_NOTIFICATION handle {handle} cookie {:016x}", + cookie + ); Ok(()) } @@ -1328,6 +1394,7 @@ impl Process { } fn deferred_flush(&self) { + binder_debug!(pid = self.task.pid(), OpenClose, "flushing process"); let inner = self.inner.lock(); for thread in inner.threads.values() { thread.exit_looper(); @@ -1335,6 +1402,8 @@ impl Process { } fn deferred_release(self: Arc<Self>) { + binder_debug!(pid = self.task.pid(), OpenClose, "releasing process"); + let is_manager = { let mut inner = self.inner.lock(); inner.is_dead = true; @@ -1382,13 +1451,11 @@ impl Process { // SAFETY: We are removing the `NodeRefInfo` from the right node. unsafe { info.node_ref2().node.remove_node_info(info) }; - // Remove all death notifications from the nodes (that belong to a different process). - let death = if let Some(existing) = info.death().take() { - existing - } else { - continue; - }; - death.set_cleared(false); + // Clear death notifications from the nodes (that belong to a different process). + // No need to remove them from `info` as we clear info below. + if let Some(death) = info.death().as_ref() { + death.set_cleared(false); + } } // Clean up freeze listeners. @@ -1524,6 +1591,15 @@ impl Process { } } } + + pub(crate) fn notify_poll(&self, sync: bool) { + if let Some(poll) = self.poll.as_ref() { + if sync { + poll.notify_sync(); + } + poll.notify_all(); + } + } } fn get_frozen_status(data: UserSlice) -> Result { @@ -1536,13 +1612,13 @@ fn get_frozen_status(data: UserSlice) -> Result { for ctx in crate::context::get_all_contexts()? { ctx.for_each_proc(|proc| { - if proc.task.pid() == info.pid as _ { + if proc.task.pid() == info.pid as Pid { found = true; let inner = proc.inner.lock(); let txns_pending = inner.txns_pending_locked(); - info.async_recv |= inner.async_recv as u32; - info.sync_recv |= inner.sync_recv as u32; - info.sync_recv |= (txns_pending as u32) << 1; + info.async_recv |= u32::from(inner.async_recv); + info.sync_recv |= u32::from(inner.sync_recv); + info.sync_recv |= u32::from(txns_pending) << 1; } }); } @@ -1634,7 +1710,9 @@ impl Process { /// The file operations supported by `Process`. impl Process { pub(crate) fn open(ctx: ArcBorrow<'_, Context>, file: &File) -> Result<Arc<Process>> { - Self::new(ctx.into(), ARef::from(file.cred())) + let proc = Self::new(ctx.into(), ARef::from(file.cred()))?; + binder_debug!(OpenClose, "opened process"); + Ok(proc) } pub(crate) fn release(this: Arc<Process>, _file: &File) { @@ -1642,8 +1720,8 @@ impl Process { let should_schedule; { let mut inner = this.inner.lock(); - should_schedule = inner.defer_work == 0; - inner.defer_work |= PROC_DEFER_RELEASE; + should_schedule = inner.defer_work == DeferWorks::empty(); + inner.defer_work |= DeferWork::Release; binderfs_file = inner.binderfs_file.take(); } @@ -1660,8 +1738,8 @@ impl Process { let should_schedule; { let mut inner = this.inner.lock(); - should_schedule = inner.defer_work == 0; - inner.defer_work |= PROC_DEFER_FLUSH; + should_schedule = inner.defer_work == DeferWorks::empty(); + inner.defer_work |= DeferWork::Flush; } if should_schedule { @@ -1719,7 +1797,21 @@ impl Process { table: PollTable<'_>, ) -> Result<u32> { let thread = this.get_current_thread()?; - let (from_proc, mut mask) = thread.poll(file, table); + { + let poll = loop { + if let Some(poll) = this.poll.as_ref() { + break poll; + } + + let poll = PollCondVarBox::new(c"Process::poll", kernel::static_lock_class!())?; + // Reuse our existing lock to synchronize callers initializing. + let _guard = this.node_refs.lock(); + this.poll.populate(poll); + }; + + table.register_wait(file, poll); + } + let (from_proc, mut mask) = thread.poll()?; if mask == 0 && from_proc && !this.inner.lock().work.is_empty() { mask |= bindings::POLLIN; } diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index d6ceebbd5f94..955c4c348f73 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -6,12 +6,6 @@ #![crate_name = "rust_binder"] #![recursion_limit = "256"] -#![allow( - clippy::as_underscore, - clippy::ref_as_ptr, - clippy::ptr_as_ptr, - clippy::cast_lossless -)] use kernel::{ bindings::{self, seq_file}, @@ -38,7 +32,10 @@ mod allocation; mod context; mod deferred_close; mod defs; +#[macro_use] +mod debug; mod error; +mod netlink; mod node; mod page_range; mod process; @@ -226,6 +223,7 @@ impl<T: ListArcSafe> DTRWrap<T> { struct DeliverCode { code: u32, skip: Atomic<bool>, + pid: i32, } kernel::list::impl_list_arc_safe! { @@ -233,10 +231,11 @@ kernel::list::impl_list_arc_safe! { } impl DeliverCode { - fn new(code: u32) -> Self { + fn new(code: u32, pid: i32) -> Self { Self { code, skip: Atomic::new(false), + pid, } } @@ -261,7 +260,15 @@ impl DeliverToRead for DeliverCode { Ok(true) } - fn cancel(self: DArc<Self>) {} + fn cancel(self: DArc<Self>) { + if !self.skip.load(Relaxed) { + binder_debug!( + pid = self.pid, + DeadTransaction, + "undelivered TRANSACTION_COMPLETE" + ); + } + } fn should_sync_wakeup(&self) -> bool { false @@ -289,19 +296,22 @@ fn ptr_align(value: usize) -> Option<usize> { // SAFETY: We call register in `init`. static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() }; -struct BinderModule {} +struct BinderModule { + _netlink: kernel::net::netlink::Registration, +} impl kernel::Module for BinderModule { fn init(_module: &'static kernel::ThisModule) -> Result<Self> { // SAFETY: The module initializer never runs twice, so we only call this once. unsafe { crate::context::CONTEXTS.init() }; + let netlink = crate::netlink::BINDER_NL_FAMILY.register()?; BINDER_SHRINKER.register(c"android-binder")?; // SAFETY: The module is being loaded, so we can initialize binderfs. unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? }; - Ok(Self {}) + Ok(Self { _netlink: netlink }) } } @@ -315,9 +325,6 @@ unsafe impl<T> Sync for AssertSync<T> {} #[no_mangle] #[used] pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = { - // SAFETY: All zeroes is safe for the `file_operations` type. - let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; - let ops = kernel::bindings::file_operations { owner: this_module::<LocalModule>().as_ptr(), poll: Some(rust_binder_poll), @@ -327,7 +334,7 @@ pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = { open: Some(rust_binder_open), release: Some(rust_binder_release), flush: Some(rust_binder_flush), - ..zeroed_ops + ..pin_init::zeroed() }; AssertSync(ops) }; @@ -418,7 +425,7 @@ unsafe extern "C" fn rust_binder_ioctl( // SAFETY: We previously set `private_data` in `rust_binder_open`. let f = unsafe { Arc::<Process>::borrow((*file).private_data) }; // SAFETY: The caller ensures that the file is valid. - match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd as _, arg as _) { + match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd, arg) { Ok(()) => 0, Err(err) => err.to_errno() as isize, } @@ -512,7 +519,7 @@ unsafe extern "C" fn rust_binder_proc_show( _: *mut kernel::ffi::c_void, ) -> kernel::ffi::c_int { // SAFETY: Accessing the private field of `seq_file` is okay. - let pid = (unsafe { (*ptr).private }) as usize as Pid; + let pid = unsafe { (*ptr).private }.addr() as Pid; // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which // this method is called. let m = unsafe { SeqFile::from_raw(ptr) }; diff --git a/drivers/android/binder/rust_binderfs.c b/drivers/android/binder/rust_binderfs.c index ade1c4d92499..300cc65562d1 100644 --- a/drivers/android/binder/rust_binderfs.c +++ b/drivers/android/binder/rust_binderfs.c @@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc); char *rust_binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES; module_param_named(rust_devices, rust_binder_devices_param, charp, 0444); +extern u32 rust_binder_debug_mask; +module_param_named(debug_mask, rust_binder_debug_mask, uint, 0644); + static dev_t binderfs_dev; static DEFINE_MUTEX(binderfs_minors_mutex); static DEFINE_IDA(binderfs_minors); diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index bc0ef8927905..18a14aa8a835 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -9,15 +9,15 @@ use kernel::{ bindings, - fs::{File, LocalFile}, + bits::bit_u32, + fs::LocalFile, list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc}, prelude::*, security, seq_file::SeqFile, seq_print, sync::atomic::{ordering::Relaxed, Atomic}, - sync::poll::{PollCondVar, PollTable}, - sync::{aref::ARef, Arc, SpinLock}, + sync::{aref::ARef, Arc, CondVar, SpinLock}, task::Task, uaccess::{UserPtr, UserSlice, UserSliceReader}, uapi, @@ -30,7 +30,7 @@ use crate::{ process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, - transaction::{Transaction, TransactionInfo}, + transaction::{Transaction, TransactionFlag, TransactionFlags, TransactionInfo}, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead, }; @@ -225,8 +225,10 @@ impl UnusedBufferSpace { } } +#[must_use] pub(crate) enum PushWorkRes { Ok, + OkNotifyPoll, FailedDead(DLArc<dyn DeliverToRead>), } @@ -234,6 +236,7 @@ impl PushWorkRes { fn is_ok(&self) -> bool { match self { PushWorkRes::Ok => true, + PushWorkRes::OkNotifyPoll => true, PushWorkRes::FailedDead(_) => false, } } @@ -243,7 +246,7 @@ impl PushWorkRes { struct InnerThread { /// Determines the looper state of the thread. It is a bit-wise combination of the constants /// prefixed with `LOOPER_`. - looper_flags: u32, + looper_flags: LooperFlags, /// Determines whether the looper should return. looper_need_return: bool, @@ -270,28 +273,38 @@ struct InnerThread { extended_error: ExtendedError, } -const LOOPER_REGISTERED: u32 = 0x01; -const LOOPER_ENTERED: u32 = 0x02; -const LOOPER_EXITED: u32 = 0x04; -const LOOPER_INVALID: u32 = 0x08; -const LOOPER_WAITING: u32 = 0x10; -const LOOPER_WAITING_PROC: u32 = 0x20; -const LOOPER_POLL: u32 = 0x40; +kernel::impl_flags!( + /// Represents multiple looper flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct LooperFlags(u32); + + /// Represents a single looper flag. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum LooperFlag { + Registered = bit_u32(0), + Entered = bit_u32(1), + Exited = bit_u32(2), + Invalid = bit_u32(3), + Waiting = bit_u32(4), + WaitingProc = bit_u32(5), + Poll = bit_u32(6), + } +); impl InnerThread { - fn new() -> Result<Self> { + fn new(pid: i32) -> Result<Self> { fn next_err_id() -> u32 { static EE_ID: Atomic<u32> = Atomic::new(0); EE_ID.fetch_add(1, Relaxed) } Ok(Self { - looper_flags: 0, + looper_flags: LooperFlags::default(), looper_need_return: false, is_dead: false, process_work_list: false, - reply_work: ThreadError::try_new()?, - return_work: ThreadError::try_new()?, + reply_work: ThreadError::try_new(pid)?, + return_work: ThreadError::try_new(pid)?, work_list: List::new(), current_transaction: None, extended_error: ExtendedError::new(next_err_id(), BR_OK, 0), @@ -310,27 +323,32 @@ impl InnerThread { fn push_work(&mut self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes { if self.is_dead { - PushWorkRes::FailedDead(work) + return PushWorkRes::FailedDead(work); + } + self.work_list.push_back(work); + self.process_work_list = true; + if self.looper_flags.contains(LooperFlag::Poll) { + PushWorkRes::OkNotifyPoll } else { - self.work_list.push_back(work); - self.process_work_list = true; PushWorkRes::Ok } } - fn push_reply_work(&mut self, code: u32) { + fn push_reply_work(&mut self, code: u32) -> PushWorkRes { if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) { work.set_error_code(code); - self.push_work(work); + self.push_work(work) } else { pr_warn!("Thread reply work is already in use."); + PushWorkRes::Ok } } fn push_return_work(&mut self, reply: u32) { if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) { work.set_error_code(reply); - self.push_work(work); + // Not notifying: Reply to current thread. + let _ = self.push_work(work); } else { pr_warn!("Thread return work is already in use."); } @@ -373,26 +391,27 @@ impl InnerThread { } fn looper_enter(&mut self) { - self.looper_flags |= LOOPER_ENTERED; - if self.looper_flags & LOOPER_REGISTERED != 0 { - self.looper_flags |= LOOPER_INVALID; + self.looper_flags |= LooperFlag::Entered; + if self.looper_flags.contains(LooperFlag::Registered) { + self.looper_flags |= LooperFlag::Invalid; } } fn looper_register(&mut self, valid: bool) { - self.looper_flags |= LOOPER_REGISTERED; - if !valid || self.looper_flags & LOOPER_ENTERED != 0 { - self.looper_flags |= LOOPER_INVALID; + self.looper_flags |= LooperFlag::Registered; + if !valid || self.looper_flags.contains(LooperFlag::Entered) { + self.looper_flags |= LooperFlag::Invalid; } } fn looper_exit(&mut self) { - self.looper_flags |= LOOPER_EXITED; + self.looper_flags |= LooperFlag::Exited; } /// Determines whether the thread is part of a pool, i.e., if it is a looper. fn is_looper(&self) -> bool { - self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0 + self.looper_flags + .contains_any(LooperFlag::Entered | LooperFlag::Registered) } /// Determines whether the thread should attempt to fetch work items from the process queue. @@ -404,7 +423,7 @@ impl InnerThread { } fn poll(&mut self) -> u32 { - self.looper_flags |= LOOPER_POLL; + self.looper_flags |= LooperFlag::Poll; if self.process_work_list || self.looper_need_return { bindings::POLLIN } else { @@ -422,7 +441,7 @@ pub(crate) struct Thread { #[pin] inner: SpinLock<InnerThread>, #[pin] - work_condvar: PollCondVar, + work_condvar: CondVar, /// Used to insert this thread into the process' `ready_threads` list. /// /// INVARIANT: May never be used for any other list than the `self.process.ready_threads`. @@ -445,7 +464,7 @@ kernel::list::impl_list_item! { impl Thread { pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> { - let inner = InnerThread::new()?; + let inner = InnerThread::new(process.task.pid())?; Arc::pin_init( try_pin_init!(Thread { @@ -453,7 +472,7 @@ impl Thread { process, task: ARef::from(&**kernel::current!()), inner <- kernel::new_spinlock!(inner, "Thread::inner"), - work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"), + work_condvar <- kernel::new_condvar!("Thread::work_condvar"), links <- ListLinks::new(), links_track <- AtomicTracker::new(), }), @@ -470,7 +489,7 @@ impl Thread { m, " thread {}: l {:02x} need_return {}\n", self.id, - inner.looper_flags, + u32::from(inner.looper_flags), inner.looper_need_return, ); } @@ -543,9 +562,9 @@ impl Thread { return Ok(Some(work)); } - inner.looper_flags |= LOOPER_WAITING; + inner.looper_flags |= LooperFlag::Waiting; let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner); - inner.looper_flags &= !LOOPER_WAITING; + inner.looper_flags &= !LooperFlag::Waiting; if signal_pending { return Err(EINTR); @@ -597,9 +616,9 @@ impl Thread { return Ok(Some(work)); } - inner.looper_flags |= LOOPER_WAITING | LOOPER_WAITING_PROC; + inner.looper_flags |= LooperFlag::Waiting | LooperFlag::WaitingProc; let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner); - inner.looper_flags &= !(LOOPER_WAITING | LOOPER_WAITING_PROC); + inner.looper_flags &= !(LooperFlag::Waiting | LooperFlag::WaitingProc); if signal_pending || inner.looper_need_return { // We need to return now. We need to pull the thread off the list of ready threads @@ -624,7 +643,14 @@ impl Thread { /// Returns whether the item was successfully pushed. This can only fail if the thread is dead. pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes { let sync = work.should_sync_wakeup(); + self.push_work_inner(work, sync) + } + pub(crate) fn push_work_inner( + &self, + work: DLArc<dyn DeliverToRead>, + sync: bool, + ) -> PushWorkRes { let res = self.inner.lock().push_work(work); if res.is_ok() { @@ -643,7 +669,8 @@ impl Thread { pub(crate) fn push_work_if_looper(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult { let mut inner = self.inner.lock(); if inner.is_looper() && !inner.is_dead { - inner.push_work(work); + // Not notifying: Reply to current thread. + let _ = inner.push_work(work); Ok(()) } else { drop(inner); @@ -673,9 +700,9 @@ impl Thread { let strong = obj.hdr.type_ == BINDER_TYPE_BINDER; // SAFETY: `binder` is a `binder_uintptr_t`; any bit pattern is a valid // representation. - let ptr = unsafe { obj.__bindgen_anon_1.binder } as _; - let cookie = obj.cookie as _; - let flags = obj.flags as _; + let ptr = unsafe { obj.__bindgen_anon_1.binder }; + let cookie = obj.cookie; + let flags = obj.flags; let node = self .process .as_arc_borrow() @@ -686,7 +713,7 @@ impl Thread { BinderObjectRef::Handle(obj) => { let strong = obj.hdr.type_ == BINDER_TYPE_HANDLE; // SAFETY: `handle` is a `u32`; any bit pattern is a valid representation. - let handle = unsafe { obj.__bindgen_anon_1.handle } as _; + let handle = unsafe { obj.__bindgen_anon_1.handle }; let node = self.process.get_node_from_handle(handle, strong)?; security::binder_transfer_binder(&self.process.cred, &view.alloc.process.cred)?; view.transfer_binder_object(offset, obj, strong, node)?; @@ -728,11 +755,12 @@ impl Thread { let alloc_offset = match sg_state.unused_buffer_space.claim_next(obj_length) { Ok(alloc_offset) => alloc_offset, Err(err) => { - pr_warn!( - "Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})", + binder_debug!( + UserError, + "failed to claim space for a BINDER_TYPE_PTR (offset: {}, limit: {}, size: {})", sg_state.unused_buffer_space.offset, sg_state.unused_buffer_space.limit, - obj_length, + obj_length ); return Err(err.into()); } @@ -743,7 +771,7 @@ impl Thread { ScatterGatherEntry { obj_index, offset: alloc_offset, - sender_uaddr: obj.buffer as _, + sender_uaddr: obj.buffer as usize, length: obj_length, pointer_fixups: KVec::new(), fixup_min_offset: 0, @@ -811,6 +839,7 @@ impl Thread { let fds_len = num_fds.checked_mul(size_of::<u32>()).ok_or(EINVAL)?; if !is_aligned(parent_offset, size_of::<u32>()) { + binder_debug!(UserError, "FDA parent offset not aligned correctly"); return Err(EINVAL.into()); } @@ -829,6 +858,7 @@ impl Thread { }; if !is_aligned(parent_entry.sender_uaddr, size_of::<u32>()) { + binder_debug!(UserError, "FDA parent buffer not aligned correctly"); return Err(EINVAL.into()); } @@ -850,7 +880,7 @@ impl Thread { .ok_or(EINVAL)?; let mut fda_bytes = KVec::new(); - UserSlice::new(UserPtr::from_addr(fda_uaddr as _), fds_len) + UserSlice::new(UserPtr::from_addr(fda_uaddr as usize), fds_len) .read_all(&mut fda_bytes, GFP_KERNEL)?; if fds_len != fda_bytes.len() { @@ -912,12 +942,9 @@ impl Thread { let target_offset_end = fixup_offset.checked_add(fixup_len).ok_or(EINVAL)?; if fixup_offset < end_of_previous_fixup || offset_end < target_offset_end { - pr_warn!( - "Fixups oob {} {} {} {}", - fixup_offset, - end_of_previous_fixup, - offset_end, - target_offset_end + binder_debug!( + UserError, + "fixups oob {fixup_offset} {end_of_previous_fixup} {offset_end} {target_offset_end}" ); return Err(EINVAL.into()); } @@ -925,18 +952,21 @@ impl Thread { let copy_off = end_of_previous_fixup; let copy_len = fixup_offset - end_of_previous_fixup; if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) { - pr_warn!("Failed copying into alloc: {:?}", err); + binder_debug!(UserError, "failed copying into alloc: {err:?}"); return Err(err.into()); } if let PointerFixupEntry::Fixup { pointer_value, .. } = fixup { let res = alloc.write::<u64>(fixup_offset, pointer_value); if let Err(err) = res { - pr_warn!("Failed copying ptr into alloc: {:?}", err); + binder_debug!(UserError, "failed copying ptr into alloc: {err:?}"); return Err(err.into()); } } if let Err(err) = reader.skip(fixup_len) { - pr_warn!("Failed skipping {} from reader: {:?}", fixup_len, err); + binder_debug!( + UserError, + "failed skipping {fixup_len} from reader: {err:?}" + ); return Err(err.into()); } end_of_previous_fixup = target_offset_end; @@ -944,7 +974,7 @@ impl Thread { let copy_off = end_of_previous_fixup; let copy_len = offset_end - end_of_previous_fixup; if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) { - pr_warn!("Failed copying remainder into alloc: {:?}", err); + binder_debug!(UserError, "failed copying remainder into alloc: {err:?}"); return Err(err.into()); } } @@ -1048,7 +1078,7 @@ impl Thread { let offset: usize = offset.try_into().map_err(|_| EINVAL)?; if offset < end_of_previous_object || !is_aligned(offset, size_of::<u32>()) { - pr_warn!("Got transaction with invalid offset."); + binder_debug!(UserError, "got transaction with invalid offset"); return Err(EINVAL.into()); } @@ -1073,7 +1103,7 @@ impl Thread { ) { Ok(()) => end_of_previous_object = offset + object.size(), Err(err) => { - pr_warn!("Error while translating object."); + binder_debug!(UserError, "error while translating object: {err:?}"); return Err(err); } } @@ -1093,15 +1123,12 @@ impl Thread { )?; if let Some(sg_state) = sg_state.as_mut() { - if let Err(err) = self.apply_sg(&mut alloc, sg_state) { - pr_warn!("Failure in apply_sg: {:?}", err); - return Err(err); - } + self.apply_sg(&mut alloc, sg_state)?; } if let Some((off_out, secctx)) = secctx.as_mut() { if let Err(err) = alloc.write(secctx_off, secctx.as_bytes()) { - pr_warn!("Failed to write security context: {:?}", err); + binder_debug!(UserError, "failed to write security context: {err:?}"); return Err(err.into()); } **off_out = secctx_off; @@ -1115,6 +1142,12 @@ impl Thread { let mut inner = thread.inner.lock(); inner.pop_transaction_to_reply(thread.as_ref()) } { + binder_debug!( + DeadTransaction, + "release transaction {} in, still active", + transaction.debug_id + ); + let reply = Err(BR_DEAD_REPLY); if !transaction .from @@ -1154,7 +1187,7 @@ impl Thread { transaction.set_outstanding(&mut self.process.inner.lock()); } - { + let ret = { let mut inner = self.inner.lock(); if !inner.pop_transaction_replied(transaction) { return false; @@ -1171,15 +1204,16 @@ impl Thread { } match reply { - Ok(work) => { - inner.push_work(work); - } + Ok(work) => inner.push_work(work), Err(code) => inner.push_reply_work(code), } - } + }; // Notify the thread now that we've released the inner lock. self.work_condvar.notify_sync(); + if matches!(ret, PushWorkRes::OkNotifyPoll) { + self.process.notify_poll(true); + } false } @@ -1232,7 +1266,7 @@ impl Thread { info.from_pid = self.process.task.pid(); info.from_tid = self.id; info.code = td.transaction_data.code; - info.flags = td.transaction_data.flags; + info.flags = TransactionFlags::from_bits(td.transaction_data.flags); info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize); info.data_size = td.transaction_data.data_size as usize; info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize); @@ -1274,16 +1308,36 @@ impl Thread { ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno()); } - pr_warn!( - "{}:{} transaction to {} failed: {err:?}", - info.from_pid, - info.from_tid, - info.to_pid + binder_debug!( + FailedTransaction, + "transaction {} to {}:{} failed {:?}, code {} size {}-{}", + if info.is_reply { + "reply" + } else if info.is_oneway() { + "async" + } else { + "call" + }, + info.to_pid, + info.to_tid, + err, + info.code, + info.data_size, + info.offsets_size ); } } } + if info.oneway_spam_suspect { + // If this is both a oneway spam suspect and a failure, we report it twice. This is + // useful in case the transaction failed with BR_TRANSACTION_PENDING_FROZEN. + info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx); + } + if info.reply != 0 { + info.report_netlink(info.reply, &self.process.ctx); + } + Ok(()) } @@ -1294,7 +1348,10 @@ impl Thread { // TODO: We need to ensure that there isn't a pending transaction in the work queue. How // could this happen? let top = self.top_of_transaction_stack()?; - let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; + let list_completion = DTRWrap::arc_try_new(DeliverCode::new( + BR_TRANSACTION_COMPLETE, + self.process.task.pid(), + ))?; let completion = list_completion.clone_arc(); let transaction = Transaction::new(node_ref, top, self, info)?; @@ -1303,7 +1360,7 @@ impl Thread { { let mut inner = self.inner.lock(); if !transaction.is_stacked_on(&inner.current_transaction) { - pr_warn!("Transaction stack changed during transaction!"); + binder_debug!(UserError, "got new transaction with bad transaction stack"); return Err(EINVAL.into()); } inner.current_transaction = Some(transaction.clone_arc()); @@ -1326,8 +1383,18 @@ impl Thread { } fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult { - let orig = self.inner.lock().pop_transaction_to_reply(self)?; + let orig = match self.inner.lock().pop_transaction_to_reply(self) { + Ok(orig) => orig, + Err(err) => { + binder_debug!(UserError, "got reply transaction with no transaction stack"); + return Err(err.into()); + } + }; if !orig.from.is_current_transaction(&orig) { + binder_debug!( + UserError, + "got reply transaction with bad transaction stack" + ); return Err(EINVAL.into()); } @@ -1336,11 +1403,15 @@ impl Thread { // We need to complete the transaction even if we cannot complete building the reply. let out = (|| -> BinderResult<_> { - let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; + let completion = DTRWrap::arc_try_new(DeliverCode::new( + BR_TRANSACTION_COMPLETE, + self.process.task.pid(), + ))?; let process = orig.from.process.clone(); - let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; + let allow_fds = orig.flags.contains(TransactionFlag::AcceptFds); let reply = Transaction::new_reply(self, process, info, allow_fds)?; - self.inner.lock().push_work(completion); + // Not notifying: Reply to current thread. + let _ = self.inner.lock().push_work(completion); orig.from.deliver_reply(Ok(reply), &orig, None); Ok(()) })() @@ -1354,11 +1425,11 @@ impl Thread { info.from_tid, info.to_pid ); - let param = err.source.as_ref().map_or(0, |e| e.to_errno()); let ee = ExtendedError::new(info.debug_id as u32, err.reply, param); orig.from .deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee)); + info.reply = BR_FAILED_REPLY; err.reply = BR_TRANSACTION_COMPLETE; err }); @@ -1376,9 +1447,11 @@ impl Thread { } else { BR_TRANSACTION_COMPLETE }; - let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?; + let list_completion = + DTRWrap::arc_try_new(DeliverCode::new(code, self.process.task.pid()))?; let completion = list_completion.clone_arc(); - self.inner.lock().push_work(list_completion); + // Not notifying: Reply to current thread. + let _ = self.inner.lock().push_work(list_completion); match transaction.submit(info) { Ok(()) => Ok(()), Err(err) => { @@ -1392,7 +1465,7 @@ impl Thread { let write_start = req.write_buffer.wrapping_add(req.write_consumed); let write_len = req.write_size.saturating_sub(req.write_consumed); let mut reader = - UserSlice::new(UserPtr::from_addr(write_start as _), write_len as _).reader(); + UserSlice::new(UserPtr::from_addr(write_start as usize), write_len as usize).reader(); while reader.len() >= size_of::<u32>() && self.inner.lock().return_work.is_unused() { let before = reader.len(); @@ -1463,7 +1536,7 @@ impl Thread { let read_start = req.read_buffer.wrapping_add(req.read_consumed); let read_len = req.read_size.saturating_sub(req.read_consumed); let mut writer = BinderReturnWriter::new( - UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(), + UserSlice::new(UserPtr::from_addr(read_start as usize), read_len as usize).writer(), self, ); let (in_pool, has_transaction, thread_todo, use_proc_queue) = { @@ -1527,9 +1600,11 @@ impl Thread { // Write BR_SPAWN_LOOPER if the process needs more threads for its pool. if has_noop_placeholder && in_pool && self.process.needs_thread() { - let mut writer = - UserSlice::new(UserPtr::from_addr(req.read_buffer as _), req.read_size as _) - .writer(); + let mut writer = UserSlice::new( + UserPtr::from_addr(req.read_buffer as usize), + req.read_size as usize, + ) + .writer(); writer.write(&BR_SPAWN_LOOPER)?; } Ok(()) @@ -1578,16 +1653,15 @@ impl Thread { ret } - pub(crate) fn poll(&self, file: &File, table: PollTable<'_>) -> (bool, u32) { - table.register_wait(file, &self.work_condvar); + pub(crate) fn poll(&self) -> Result<(bool, u32)> { let mut inner = self.inner.lock(); - (inner.should_use_process_work_queue(), inner.poll()) + Ok((inner.should_use_process_work_queue(), inner.poll())) } /// Make the call to `get_work` or `get_work_local` return immediately, if any. pub(crate) fn exit_looper(&self) { let mut inner = self.inner.lock(); - let should_notify = inner.looper_flags & LOOPER_WAITING != 0; + let should_notify = inner.looper_flags.contains(LooperFlag::Waiting); if should_notify { inner.looper_need_return = true; } @@ -1598,26 +1672,9 @@ impl Thread { } } - pub(crate) fn notify_if_poll_ready(&self, sync: bool) { - // Determine if we need to notify. This requires the lock. - let inner = self.inner.lock(); - let notify = inner.looper_flags & LOOPER_POLL != 0 && inner.should_use_process_work_queue(); - drop(inner); - - // Now that the lock is no longer held, notify the waiters if we have to. - if notify { - if sync { - self.work_condvar.notify_sync(); - } else { - self.work_condvar.notify_one(); - } - } - } - pub(crate) fn release(self: &Arc<Self>) { self.inner.lock().is_dead = true; - //self.work_condvar.clear(); self.unwind_transaction_stack(); // Cancel all pending work items. @@ -1630,14 +1687,16 @@ impl Thread { #[pin_data] struct ThreadError { error_code: Atomic<u32>, + pid: i32, #[pin] links_track: AtomicTracker, } impl ThreadError { - fn try_new() -> Result<DArc<Self>> { + fn try_new(pid: i32) -> Result<DArc<Self>> { DTRWrap::arc_pin_init(pin_init!(Self { error_code: Atomic::new(BR_OK), + pid, links_track <- AtomicTracker::new(), })) .map(ListArc::into_arc) @@ -1664,7 +1723,16 @@ impl DeliverToRead for ThreadError { Ok(true) } - fn cancel(self: DArc<Self>) {} + fn cancel(self: DArc<Self>) { + let code = self.error_code.load(Relaxed); + if code != BR_OK { + binder_debug!( + pid = self.pid, + DeadTransaction, + "undelivered TRANSACTION_ERROR: {code}" + ); + } + } fn should_sync_wakeup(&self) -> bool { false diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 5539672d7285..06aabb3cc2f1 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -4,6 +4,8 @@ use crate::transaction::Transaction; +use core::ptr; + use kernel::bindings::{rust_binder_transaction, task_struct}; use kernel::error::Result; use kernel::ffi::{c_int, c_uint, c_ulong}; @@ -26,7 +28,7 @@ declare_trace! { #[inline] fn raw_transaction(t: &Transaction) -> rust_binder_transaction { - t as *const Transaction as rust_binder_transaction + ptr::from_ref(t).cast_mut().cast() } #[inline] diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 0e5d07b7e6f0..245f1556b5db 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -3,6 +3,7 @@ // Copyright (C) 2025 Google LLC. use kernel::{ + net::netlink::GENLMSG_DEFAULT_SIZE, prelude::*, seq_file::SeqFile, seq_print, @@ -11,12 +12,14 @@ use kernel::{ task::{Kuid, Pid}, time::{Instant, Monotonic}, types::ScopeGuard, + uapi, }; use crate::{ allocation::{Allocation, TranslatedFds}, defs::*, error::{BinderError, BinderResult}, + netlink::Report, node::{Node, NodeRef}, process::{Process, ProcessInner}, ptr_align, @@ -24,6 +27,33 @@ use crate::{ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead, }; +kernel::impl_flags!( + /// Represents multiple transaction flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Zeroable)] + pub struct TransactionFlags(u32); + + /// Represents a single transaction flag. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum TransactionFlag { + OneWay = TF_ONE_WAY, + AcceptFds = TF_ACCEPT_FDS, + ClearBuf = TF_CLEAR_BUF, + UpdateTxn = TF_UPDATE_TXN, + } +); + +impl TransactionFlags { + /// Creates a `TransactionFlags` from a raw `u32` value. + pub(crate) fn from_bits(bits: u32) -> Self { + Self(bits) + } + + /// Checks if the Oneway flag is set. + pub(crate) fn is_oneway(self) -> bool { + self.contains(TransactionFlag::OneWay) + } +} + #[derive(Zeroable)] pub(crate) struct TransactionInfo { pub(crate) from_pid: Pid, @@ -31,7 +61,7 @@ pub(crate) struct TransactionInfo { pub(crate) to_pid: Pid, pub(crate) to_tid: Pid, pub(crate) code: u32, - pub(crate) flags: u32, + pub(crate) flags: TransactionFlags, pub(crate) data_ptr: UserPtr, pub(crate) data_size: usize, pub(crate) offsets_ptr: UserPtr, @@ -48,7 +78,45 @@ pub(crate) struct TransactionInfo { impl TransactionInfo { #[inline] pub(crate) fn is_oneway(&self) -> bool { - self.flags & TF_ONE_WAY != 0 + self.flags.is_oneway() + } + + pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) { + if let Err(err) = self.report_netlink_inner(reply, ctx) { + pr_warn!( + "{}:{} netlink report failed: {err:?}\n", + self.from_pid, + self.from_tid + ); + } + } + + fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> kernel::error::Result { + if !Report::has_listeners() { + return Ok(()); + } + let mut report = Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KERNEL)?; + + report.error(reply)?; + report.context(&ctx.name)?; + report.from_pid(self.from_pid as u32)?; + report.from_tid(self.from_tid as u32)?; + if self.to_pid != 0 { + report.to_pid(self.to_pid as u32)?; + } + if self.to_tid != 0 { + report.to_tid(self.to_tid as u32)?; + } + + if self.is_reply { + report.is_reply()?; + } + report.flags(u32::from(self.flags))?; + report.code(self.code)?; + report.data_size(self.data_size as u32)?; + + report.multicast(0, GFP_KERNEL)?; + Ok(()) } } @@ -74,7 +142,7 @@ pub(crate) struct Transaction { allocation: SpinLock<Option<Allocation>>, is_outstanding: Atomic<bool>, code: u32, - pub(crate) flags: u32, + pub(crate) flags: TransactionFlags, data_size: usize, offsets_size: usize, data_address: usize, @@ -120,7 +188,7 @@ impl Transaction { } alloc.set_info_oneway_node(node_ref.node.clone()); } - if info.flags & TF_CLEAR_BUF != 0 { + if info.flags.contains(TransactionFlag::ClearBuf) { alloc.set_info_clear_on_drop(); } let target_node = node_ref.node.clone(); @@ -160,7 +228,7 @@ impl Transaction { return Err(err); } }; - if info.flags & TF_CLEAR_BUF != 0 { + if info.flags.contains(TransactionFlag::ClearBuf) { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { @@ -193,7 +261,7 @@ impl Transaction { self.from.id, self.to.task.pid(), self.code, - self.flags, + u32::from(self.flags), self.start_time.elapsed().as_millis(), ); if let Some(target_node) = &self.target_node { @@ -272,7 +340,7 @@ impl Transaction { let _t_outdated; let _oneway_node; - let oneway = self.flags & TF_ONE_WAY != 0; + let oneway = self.flags.is_oneway(); let process = self.to.clone(); let mut process_inner = process.inner.lock(); @@ -283,7 +351,7 @@ impl Transaction { crate::trace::trace_transaction(false, &self, None); if process_inner.is_frozen.is_frozen() { process_inner.async_recv = true; - if self.flags & TF_UPDATE_TXN != 0 { + if self.flags.contains(TransactionFlag::UpdateTxn) { if let Some(t_outdated) = target_node.take_outdated_transaction(&self, &mut process_inner) { @@ -330,11 +398,15 @@ impl Transaction { crate::trace::trace_transaction(false, &self, Some(&thread.task)); match thread.push_work(self) { PushWorkRes::Ok => Ok(()), + PushWorkRes::OkNotifyPoll => { + process.notify_poll(true); + Ok(()) + } PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)), } } else { crate::trace::trace_transaction(false, &self, None); - process_inner.push_work(self) + process_inner.push_work(&process, self) }; drop(process_inner); @@ -354,7 +426,8 @@ impl Transaction { return false; } - if self.flags & old.flags & (TF_ONE_WAY | TF_UPDATE_TXN) != (TF_ONE_WAY | TF_UPDATE_TXN) { + let required = TransactionFlag::OneWay | TransactionFlag::UpdateTxn; + if !(self.flags.contains_all(required) && old.flags.contains_all(required)) { return false; } @@ -391,7 +464,7 @@ impl DeliverToRead for Transaction { writer: &mut BinderReturnWriter<'_>, ) -> Result<bool> { let send_failed_reply = ScopeGuard::new(|| { - if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { + if self.target_node.is_some() && !self.flags.is_oneway() { let reply = Err(BR_FAILED_REPLY); self.from.deliver_reply(reply, &self, None); } @@ -403,6 +476,14 @@ impl DeliverToRead for Transaction { } else { // On failure to process the list, we send a reply back to the sender and ignore the // transaction on the recipient. + binder_debug!( + FailedTransaction, + "transaction {} to {} failed, fd fixups failed, size {}-{}", + self.debug_id, + self.to.task.pid(), + self.data_size, + self.offsets_size + ); return Ok(true); }; @@ -410,20 +491,21 @@ impl DeliverToRead for Transaction { let tr = tr_sec.tr_data(); if let Some(target_node) = &self.target_node { let (ptr, cookie) = target_node.get_id(); - tr.target.ptr = ptr as _; - tr.cookie = cookie as _; + tr.target.ptr = ptr as uapi::binder_uintptr_t; + tr.cookie = cookie as uapi::binder_uintptr_t; }; tr.code = self.code; - tr.flags = self.flags; - tr.data_size = self.data_size as _; - tr.data.ptr.buffer = self.data_address as _; - tr.offsets_size = self.offsets_size as _; + tr.flags = u32::from(self.flags); + tr.data_size = self.data_size as uapi::binder_size_t; + tr.data.ptr.buffer = self.data_address as uapi::binder_uintptr_t; + tr.offsets_size = self.offsets_size as uapi::binder_size_t; if tr.offsets_size > 0 { - tr.data.ptr.offsets = (self.data_address + ptr_align(self.data_size).unwrap()) as _; + tr.data.ptr.offsets = + (self.data_address + ptr_align(self.data_size).unwrap()) as uapi::binder_uintptr_t; } tr.sender_euid = self.sender_euid.into_uid_in_current_ns(); tr.sender_pid = 0; - if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { + if self.target_node.is_some() && !self.flags.is_oneway() { // Not a reply and not one-way. tr.sender_pid = self.from.process.pid_in_current_ns(); } @@ -475,16 +557,23 @@ impl DeliverToRead for Transaction { drop(allocation); // If this is not a reply or oneway transaction, then send a dead reply. - if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { + if self.target_node.is_some() && !self.flags.is_oneway() { let reply = Err(BR_DEAD_REPLY); self.from.deliver_reply(reply, &self, None); + } else { + binder_debug!( + pid = self.to.task.pid(), + DeadTransaction, + "undelivered transaction {}, process died", + self.debug_id + ); } self.drop_outstanding_txn(); } fn should_sync_wakeup(&self) -> bool { - self.flags & TF_ONE_WAY == 0 + !self.flags.is_oneway() } fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result<()> { diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 70701d3bc81c..9f9c72727a05 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -401,9 +401,14 @@ static int cache_setup_properties(unsigned int cpu) else if (!acpi_disabled) ret = cache_setup_acpi(cpu); - // Assume there is no cache information available in DT/ACPI from now. - if (ret && use_arch_cache_info()) + /* + * No DT/ACPI cache nodes; fall back to arch-derived topology (e.g. + * arm64 CLIDR_EL1) and clear the error to avoid a spurious warning. + */ + if (ret && use_arch_cache_info()) { use_arch_info = true; + ret = 0; + } return ret; } diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index b1213786f72c..db7ee6547eef 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1026,26 +1026,37 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl) struct mhi_ep_chan *mhi_chan; int i; - /* Stop all the channels */ + /* Disable all the channels to prevent new transfers */ + for (i = 0; i < mhi_cntrl->max_chan; i++) { + mhi_chan = &mhi_cntrl->mhi_chan[i]; + if (!mhi_chan->ring.started) + continue; + + mutex_lock(&mhi_chan->lock); + mhi_chan->state = MHI_CH_STATE_DISABLED; + mutex_unlock(&mhi_chan->lock); + } + + /* Drain ring workers and in-flight transfers before notifying disconnect */ + flush_workqueue(mhi_cntrl->wq); + if (mhi_cntrl->flush_async) + mhi_cntrl->flush_async(mhi_cntrl); + + /* Send channel disconnect status to client drivers */ for (i = 0; i < mhi_cntrl->max_chan; i++) { mhi_chan = &mhi_cntrl->mhi_chan[i]; if (!mhi_chan->ring.started) continue; mutex_lock(&mhi_chan->lock); - /* Send channel disconnect status to client drivers */ if (mhi_chan->xfer_cb) { result.transaction_status = -ENOTCONN; result.bytes_xferd = 0; mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); } - - mhi_chan->state = MHI_CH_STATE_DISABLED; mutex_unlock(&mhi_chan->lock); } - flush_workqueue(mhi_cntrl->wq); - /* Destroy devices associated with all channels */ device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device); @@ -1340,14 +1351,19 @@ static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id) ret = dev_set_name(&mhi_dev->dev, "%s_%s", dev_name(&mhi_cntrl->mhi_dev->dev), mhi_dev->name); - if (ret) { - put_device(&mhi_dev->dev); - return ret; - } + if (ret) + goto err_put_channels; ret = device_add(&mhi_dev->dev); if (ret) - put_device(&mhi_dev->dev); + goto err_put_channels; + + return 0; + +err_put_channels: + put_device(&mhi_dev->dev); /* DL channel reference */ + put_device(&mhi_dev->dev); /* UL channel reference */ + put_device(&mhi_dev->dev); /* device_initialize() reference */ return ret; } @@ -1614,6 +1630,7 @@ static void mhi_ep_remove(struct device *dev) { struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev); struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver); + struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl; struct mhi_result result = {}; struct mhi_ep_chan *mhi_chan; int dir; @@ -1622,6 +1639,22 @@ static void mhi_ep_remove(struct device *dev) if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER) return; + /* Disable the channels to prevent new transfers */ + for (dir = 0; dir < 2; dir++) { + mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; + + if (!mhi_chan) + continue; + + mutex_lock(&mhi_chan->lock); + mhi_chan->state = MHI_CH_STATE_DISABLED; + mutex_unlock(&mhi_chan->lock); + } + + /* Flush in-flight transfers before notifying disconnect */ + if (mhi_cntrl->flush_async) + mhi_cntrl->flush_async(mhi_cntrl); + /* Disconnect the channels associated with the driver */ for (dir = 0; dir < 2; dir++) { mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; @@ -1637,7 +1670,6 @@ static void mhi_ep_remove(struct device *dev) mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); } - mhi_chan->state = MHI_CH_STATE_DISABLED; mhi_chan->xfer_cb = NULL; mutex_unlock(&mhi_chan->lock); } diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 12dcb1a2753c..fd3050889412 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -1029,7 +1029,7 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, if (mhi_cntrl->edl_trigger) { ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr); if (ret) - goto err_release_dev; + goto err_del_dev; } mhi_cntrl->mhi_dev = mhi_dev; @@ -1038,6 +1038,8 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, return 0; +err_del_dev: + device_del(&mhi_dev->dev); err_release_dev: put_device(&mhi_dev->dev); error_setup_irq: diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 53c0ffe30070..4d458396233a 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -170,6 +170,9 @@ EXPORT_SYMBOL_GPL(mhi_get_mhi_state); void mhi_soc_reset(struct mhi_controller *mhi_cntrl) { + int __maybe_unused ret; + u32 tmp; + if (mhi_cntrl->reset) { mhi_cntrl->reset(mhi_cntrl); return; @@ -178,6 +181,9 @@ void mhi_soc_reset(struct mhi_controller *mhi_cntrl) /* Generic MHI SoC reset */ mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET, MHI_SOC_RESET_REQ); + /* Flush the posted write to the device (ignore return value) */ + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET, + &tmp); } EXPORT_SYMBOL_GPL(mhi_soc_reset); diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 0d0d9c7ffa4b..b636e2c23b4d 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -491,6 +491,8 @@ static const struct mhi_pci_dev_info mhi_quectel_rm5xx_info = { static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = { MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0), MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0), + MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0), MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0), @@ -506,6 +508,8 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = { static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = { MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0), MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0), + MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0), MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0), @@ -1395,6 +1399,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(dma_data_width); mhi_cntrl->fw_image = info->fw; mhi_cntrl->edl_image = info->edl; + mhi_cntrl->no_m3 = info->no_m3; mhi_cntrl->read_reg = mhi_pci_read_reg; mhi_cntrl->write_reg = mhi_pci_write_reg; diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index f799503c8f36..ced83fbb1a51 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -651,21 +651,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) /* Trigger MHI RESET so that the device will not access host memory */ if (reset_device) { - u32 in_reset = -1; - unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms); - dev_dbg(dev, "Triggering MHI Reset in device\n"); mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); /* Wait for the reset bit to be cleared by the device */ - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_read_reg_field(mhi_cntrl, - mhi_cntrl->regs, - MHICTRL, - MHICTRL_RESET_MASK, - &in_reset) || - !in_reset, timeout); - if (!ret || in_reset) { + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, + MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms); + if (ret) { dev_err(dev, "Device failed to exit MHI Reset state\n"); write_lock_irq(&mhi_cntrl->pm_lock); cur_state = mhi_tryset_pm_state(mhi_cntrl, @@ -922,22 +914,39 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) return -EIO; } - /* Set MHI to M3 and wait for completion */ - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3); - write_unlock_irq(&mhi_cntrl->pm_lock); - dev_dbg(dev, "Waiting for M3 completion\n"); - - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->dev_state == MHI_STATE_M3 || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); + /* + * For devices without M3 support, just set the host state to M3. This + * host transition is needed to prevent the client drivers from + * accessing the device during suspend. + */ + if (mhi_cntrl->no_m3) { + new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3); + write_unlock_irq(&mhi_cntrl->pm_lock); + if (new_state != MHI_PM_M3) { + dev_err(dev, + "Error setting to PM state: %s from: %s\n", + to_mhi_pm_state_str(MHI_PM_M3), + to_mhi_pm_state_str(mhi_cntrl->pm_state)); + return -EIO; + } + } else { + /* Set MHI to M3 and wait for completion */ + mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3); + write_unlock_irq(&mhi_cntrl->pm_lock); + dev_dbg(dev, "Waiting for M3 completion\n"); - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - dev_err(dev, - "Did not enter M3 state, MHI state: %s, PM state: %s\n", - mhi_state_str(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return -EIO; + ret = wait_event_timeout(mhi_cntrl->state_event, + mhi_cntrl->dev_state == MHI_STATE_M3 || + MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), + msecs_to_jiffies(mhi_cntrl->timeout_ms)); + + if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { + dev_err(dev, + "Did not enter M3 state, MHI state: %s, PM state: %s\n", + mhi_state_str(mhi_cntrl->dev_state), + to_mhi_pm_state_str(mhi_cntrl->pm_state)); + return -EIO; + } } /* Notify clients about entering LPM */ @@ -969,7 +978,8 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) return -EIO; - if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) { + if (!mhi_cntrl->no_m3 && + mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) { dev_warn(dev, "Resuming from non M3 state (%s)\n", mhi_state_str(mhi_get_mhi_state(mhi_cntrl))); if (!force) @@ -995,6 +1005,15 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) return -EIO; } + /* + * For devices without M3 support, just move the host back to M0 + * directly. + */ + if (mhi_cntrl->no_m3) { + write_unlock_irq(&mhi_cntrl->pm_lock); + return mhi_pm_m0_transition(mhi_cntrl); + } + /* Set MHI to M0 and wait for completion */ mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0); write_unlock_irq(&mhi_cntrl->pm_lock); diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index d3d230247262..a118a89a7779 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -738,7 +738,6 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num) sysfs_bin_attr_init(res_attr); - cdx_dev->res_attr[num] = res_attr; sprintf(res_attr_name, "resource%d", num); res_attr->mmap = cdx_mmap_resource; @@ -747,8 +746,12 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num) res_attr->size = cdx_resource_len(cdx_dev, num); res_attr->private = (void *)(unsigned long)num; ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr); - if (ret) + if (ret) { kfree(res_attr); + return ret; + } + + cdx_dev->res_attr[num] = res_attr; return ret; } diff --git a/drivers/char/powernv-op-panel.c b/drivers/char/powernv-op-panel.c index 63175b765c90..a98acb199805 100644 --- a/drivers/char/powernv-op-panel.c +++ b/drivers/char/powernv-op-panel.c @@ -89,7 +89,6 @@ static int __op_panel_update_display(void) static ssize_t oppanel_write(struct file *filp, const char __user *userbuf, size_t len, loff_t *f_pos) { - loff_t f_pos_prev = *f_pos; ssize_t ret; int rc; @@ -105,7 +104,6 @@ static ssize_t oppanel_write(struct file *filp, const char __user *userbuf, if (rc != OPAL_SUCCESS) { pr_err_ratelimited("OPAL call failed to write to op panel display [rc=%d]\n", rc); - *f_pos = f_pos_prev; return -EIO; } } diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 6da817b9849f..8803268b4cdc 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -340,15 +340,17 @@ static enum ieee1284_phase init_phase(int mode) return IEEE1284_PH_FWD_IDLE; } -static int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec) +static int pp_set_timeout(struct pardevice *pdev, s64 tv_sec, s64 tv_usec) { + struct timespec64 ts; long to_jiffies; - if ((tv_sec < 0) || (tv_usec < 0)) + if (tv_sec < 0 || tv_usec < 0 || tv_usec >= USEC_PER_SEC) return -EINVAL; - to_jiffies = usecs_to_jiffies(tv_usec); - to_jiffies += tv_sec * HZ; + ts.tv_sec = tv_sec; + ts.tv_nsec = tv_usec * NSEC_PER_USEC; + to_jiffies = timespec64_to_jiffies(&ts); if (to_jiffies <= 0) return -EINVAL; diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index dd45fe5eb6f2..255f69123af5 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -328,7 +328,7 @@ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static ssize_t store_received_ref_clk3a(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -350,7 +350,7 @@ static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_received_ref_clk3b(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -372,7 +372,7 @@ static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_enable_clk3b_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -394,7 +394,7 @@ static ssize_t store_enable_clk3a_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -415,7 +415,7 @@ static ssize_t store_enable_clkb1_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -437,7 +437,7 @@ static ssize_t store_enable_clka1_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -458,7 +458,7 @@ static ssize_t store_enable_clkb0_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -479,7 +479,7 @@ static ssize_t store_enable_clka0_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -500,7 +500,7 @@ static ssize_t store_select_amcb2_transmit_clock(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -541,7 +541,7 @@ static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_select_amcb1_transmit_clock(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -583,7 +583,7 @@ static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_select_redundant_clock(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -604,7 +604,7 @@ static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_select_ref_frequency(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -625,7 +625,7 @@ static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_filter_select(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -645,7 +645,7 @@ static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select); static ssize_t store_hardware_switching_mode(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -666,7 +666,7 @@ static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_hardware_switching(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -687,7 +687,7 @@ static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_refalign (struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned long flags; sscanf(buf, "%lX", &tmp); @@ -706,7 +706,7 @@ static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign); static ssize_t store_mode_select (struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -726,7 +726,7 @@ static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select); static ssize_t store_reset (struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 198b97314168..62eecfa61646 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -157,6 +157,12 @@ struct ports_device { /* Major number for this device. Ports will be created as minors. */ int chr_major; + + /* + * Set to true during PM freeze to block TX paths that may race + * with virtqueue teardown (e.g. hvc put_chars with no_console_suspend). + */ + bool pm_freezing; }; struct port_stats { @@ -304,6 +310,12 @@ out: return port; } +/* + * Finds a port by the virtqueue and returns a pointer to struct port + * with the reference count incremented. + * + * Callers MUST decrement it when finished. + */ static struct port *find_port_by_vq(struct ports_device *portdev, struct virtqueue *vq) { @@ -312,8 +324,10 @@ static struct port *find_port_by_vq(struct ports_device *portdev, spin_lock_irqsave(&portdev->ports_lock, flags); list_for_each_entry(port, &portdev->ports, list) - if (port->in_vq == vq || port->out_vq == vq) + if (port->in_vq == vq || port->out_vq == vq) { + kref_get(&port->kref); goto out; + } port = NULL; out: spin_unlock_irqrestore(&portdev->ports_lock, flags); @@ -402,7 +416,7 @@ static void reclaim_dma_bufs(void) } static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size, - int pages) + int pages, gfp_t gfp) { struct port_buffer *buf; @@ -436,11 +450,10 @@ static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size /* Increase device refcnt to avoid freeing it */ get_device(buf->dev); - buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma, - GFP_KERNEL); + buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma, gfp); } else { buf->dev = NULL; - buf->buf = kmalloc(buf_size, GFP_KERNEL); + buf->buf = kmalloc(buf_size, gfp); } if (!buf->buf) @@ -595,27 +608,46 @@ static void reclaim_consumed_buffers(struct port *port) static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, int nents, size_t in_count, - void *data, bool nonblock) + struct port_buffer *buf, bool nonblock) { struct virtqueue *out_vq; int err; unsigned long flags; unsigned int len; - - out_vq = port->out_vq; + struct ports_device *portdev; spin_lock_irqsave(&port->outvq_lock, flags); + portdev = READ_ONCE(port->portdev); + + if (!portdev) { + in_count = 0; + goto free_and_done; + } + + /* + * Check freeze flag under the lock so that the flag check and + * virtqueue_add_outbuf() are atomic with respect to + * remove_port_data() which also takes outvq_lock. This + * guarantees that once remove_port_data() returns, no new + * buffers can be added before remove_vqs() tears down the vq. + * Pairs with smp_store_release() in virtcons_freeze/restore. + */ + if (smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */ + goto free_and_done; + + out_vq = port->out_vq; + reclaim_consumed_buffers(port); - err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC); + err = virtqueue_add_outbuf(out_vq, sg, nents, buf, GFP_ATOMIC); /* Tell Host to go! */ virtqueue_kick(out_vq); if (err) { in_count = 0; - goto done; + goto free_and_done; } if (out_vq->num_free == 0) @@ -632,10 +664,19 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, * buffer and relax the spinning requirement. The downside is * we need to kmalloc a GFP_ATOMIC buffer each time the * console driver writes something out. + * + * Spin until host returns the buffer. + * Capture the returned buf so we can free it. + * If broken, buf == NULL and buf stays in the vq; + * remove_vqs() will call virtqueue_detach_unused_buf() -> free_buf(). */ - while (!virtqueue_get_buf(out_vq, &len) + while (!(buf = virtqueue_get_buf(out_vq, &len)) && !virtqueue_is_broken(out_vq)) cpu_relax(); + +free_and_done: + if (buf) + free_buf(buf, false); done: spin_unlock_irqrestore(&port->outvq_lock, flags); @@ -816,14 +857,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, count = min((size_t)(32 * 1024), count); - buf = alloc_buf(port->portdev->vdev, count, 0); + buf = alloc_buf(port->portdev->vdev, count, 0, GFP_KERNEL); if (!buf) return -ENOMEM; ret = copy_from_user(buf->buf, ubuf, count); if (ret) { - ret = -EFAULT; - goto free_buf; + free_buf(buf, true); + return -EFAULT; } /* @@ -835,15 +876,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, */ nonblock = true; sg_init_one(sg, buf->buf, count); - ret = __send_to_port(port, sg, 1, count, buf, nonblock); - - if (nonblock && ret > 0) - goto out; - -free_buf: - free_buf(buf, true); -out: - return ret; + return __send_to_port(port, sg, 1, count, buf, nonblock); } struct sg_list { @@ -932,7 +965,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, goto error_out; occupancy = pipe_buf_usage(pipe); - buf = alloc_buf(port->portdev->vdev, 0, occupancy); + buf = alloc_buf(port->portdev->vdev, 0, occupancy, GFP_KERNEL); if (!buf) { ret = -ENOMEM; @@ -946,11 +979,12 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, sg_init_table(sgl.sg, sgl.size); ret = __splice_from_pipe(pipe, &sd, pipe_to_sg); pipe_unlock(pipe); + if (likely(ret > 0)) ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true); - - if (unlikely(ret <= 0)) + else free_buf(buf, true); + return ret; error_out: @@ -1108,21 +1142,50 @@ static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count) { struct port *port; struct scatterlist sg[1]; - void *data; - int ret; + struct port_buffer *pbuf; + struct ports_device *portdev; port = find_port_by_vtermno(vtermno); if (!port) return -EPIPE; - data = kmemdup(buf, count, GFP_ATOMIC); - if (!data) + /* + * Silently drop output in two cases, both by returning count so + * that the hvc layer does not spin-retry: + * + * 1. Device hot-unplug (!portdev): portdev was NULLed by + * unplug_port() after hvc_remove() was already called, so + * the hvc layer will stop invoking put_chars() very soon. + * Returning count avoids a pointless retry loop in the + * interim. + * + * 2. PM freeze (pm_freezing): the hvc console stays active + * under no_console_suspend but virtqueues are being torn + * down. Drop the output silently so the hvc layer does not + * stall suspend. + * + * This early check avoids a pointless GFP_ATOMIC allocation; + * __send_to_port() rechecks under outvq_lock for correctness. + * Pairs with smp_store_release() in virtcons_freeze/restore. + */ + portdev = READ_ONCE(port->portdev); + if (!portdev || + smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */ + return count; + + pbuf = alloc_buf(portdev->vdev, count, 0, GFP_ATOMIC); + if (!pbuf) return -ENOMEM; - sg_init_one(sg, data, count); - ret = __send_to_port(port, sg, 1, count, data, false); - kfree(data); - return ret; + memcpy(pbuf->buf, buf, count); + pbuf->len = count; + sg_init_one(sg, pbuf->buf, count); + + /* + * Ownership of pbuf is transferred to __send_to_port(). + * Do not touch or free pbuf after this call. + */ + return __send_to_port(port, sg, 1, count, pbuf, false); } /* @@ -1295,7 +1358,7 @@ static int fill_queue(struct virtqueue *vq, spinlock_t *lock) nr_added_bufs = 0; do { - buf = alloc_buf(vq->vdev, PAGE_SIZE, 0); + buf = alloc_buf(vq->vdev, PAGE_SIZE, 0, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -1498,11 +1561,16 @@ static void unplug_port(struct port *port) remove_port_data(port); /* - * We should just assume the device itself has gone off -- - * else a close on an open port later will try to send out a - * control message. + * Null out portdev under outvq_lock so that __send_to_port() + * cannot race: it checks port->portdev inside the same lock + * and bails out if NULL, preventing any buffer from being + * enqueued to an already torn-down virtqueue. Also prevents + * a close on an open port later from sending a stale control + * message. */ + spin_lock_irq(&port->outvq_lock); port->portdev = NULL; + spin_unlock_irq(&port->outvq_lock); sysfs_remove_group(&port->dev->kobj, &port_attribute_group); device_destroy(&port_class, port->dev->devt); @@ -1536,7 +1604,8 @@ static void handle_control_message(struct virtio_device *vdev, cpkt->event != cpu_to_virtio16(vdev, VIRTIO_CONSOLE_PORT_ADD)) { /* No valid header at start of buffer. Drop it. */ dev_dbg(&portdev->vdev->dev, - "Invalid index %u in control packet\n", cpkt->id); + "Invalid index %u in control packet\n", + virtio32_to_cpu(vdev, cpkt->id)); return; } @@ -1553,7 +1622,8 @@ static void handle_control_message(struct virtio_device *vdev, dev_warn(&portdev->vdev->dev, "Request for adding port with " "out-of-bound id %u, max. supported id: %u\n", - cpkt->id, portdev->max_nr_ports - 1); + virtio32_to_cpu(vdev, cpkt->id), + portdev->max_nr_ports - 1); break; } add_port(portdev, virtio32_to_cpu(vdev, cpkt->id)); @@ -1706,6 +1776,7 @@ static void out_intr(struct virtqueue *vq) } wake_up_interruptible(&port->waitqueue); + kref_put(&port->kref, remove_port); } static void in_intr(struct virtqueue *vq) @@ -1751,6 +1822,8 @@ static void in_intr(struct virtqueue *vq) if (is_console_port(port) && hvc_poll(port->cons.hvc)) hvc_kick(); + + kref_put(&port->kref, remove_port); } static void control_intr(struct virtqueue *vq) @@ -1980,6 +2053,7 @@ static int virtcons_probe(struct virtio_device *vdev) /* Attach this portdev to this virtio_device, and vice-versa. */ portdev->vdev = vdev; vdev->priv = portdev; + portdev->pm_freezing = false; portdev->chr_major = register_chrdev(0, "virtio-portsdev", &portdev_fops); @@ -2099,9 +2173,30 @@ static int virtcons_freeze(struct virtio_device *vdev) { struct ports_device *portdev; struct port *port; + unsigned long flags; portdev = vdev->priv; + /* + * Block TX paths (put_chars, __send_to_port) before resetting the + * device and tearing down virtqueues. This prevents races with + * hvc console writes that remain active under no_console_suspend. + */ + smp_store_release(&portdev->pm_freezing, true); + + /* + * Synchronize with any concurrent __send_to_port() that may have + * passed the pm_freezing check. By acquiring and releasing the + * outvq_lock for each port, we ensure all active TX paths have + * completed before we reset the device. + */ + spin_lock_irqsave(&portdev->ports_lock, flags); + list_for_each_entry(port, &portdev->ports, list) { + spin_lock(&port->outvq_lock); + spin_unlock(&port->outvq_lock); + } + spin_unlock_irqrestore(&portdev->ports_lock, flags); + virtio_reset_device(vdev); if (use_multiport(portdev)) @@ -2144,9 +2239,6 @@ static int virtcons_restore(struct virtio_device *vdev) virtio_device_ready(portdev->vdev); - if (use_multiport(portdev)) - fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); - list_for_each_entry(port, &portdev->ports, list) { port->in_vq = portdev->in_vqs[port->id]; port->out_vq = portdev->out_vqs[port->id]; @@ -2163,6 +2255,24 @@ static int virtcons_restore(struct virtio_device *vdev) if (port->guest_connected) send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); } + + /* + * Populate the control receive queue only after the list iteration + * is complete. If we fill this queue before iterating, the host could + * immediately deliver a VIRTIO_CONSOLE_PORT_REMOVE message. + * This would trigger the control workqueue, which modifies the + * portdev->ports list concurrently with the unprotected loop above, + * leading to a Use-After-Free and list corruption. + */ + if (use_multiport(portdev)) + fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); + + /* + * Allow TX paths only after all port->out_vq pointers have + * been reassigned to the newly allocated virtqueues. + */ + smp_store_release(&portdev->pm_freezing, false); + return 0; } #endif diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 34a345dc5e72..9bb5fa642fd8 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -760,7 +760,7 @@ static int __init hwicap_module_init(void) HWICAP_DEVICES, DRIVER_NAME); if (retval < 0) - return retval; + goto failed_class; retval = platform_driver_register(&hwicap_platform_driver); if (retval) @@ -771,6 +771,9 @@ static int __init hwicap_module_init(void) failed: unregister_chrdev_region(devt, HWICAP_DEVICES); + failed_class: + class_unregister(&icap_class); + return retval; } diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 1a1e64133315..46e1046abfca 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -55,11 +55,8 @@ static int xilly_drv_probe(struct platform_device *op) rc = devm_request_irq(dev, irq, xillybus_isr, 0, xillyname, endpoint); - if (rc) { - dev_err(endpoint->dev, - "Failed to register IRQ handler. Aborting.\n"); + if (rc) return -ENODEV; - } return xillybus_endpoint_discovery(endpoint); } diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index 9858711e3e79..32064b6c7627 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -83,11 +83,8 @@ static int xilly_probe(struct pci_dev *pdev, } rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0, xillyname, endpoint); - if (rc) { - dev_err(endpoint->dev, - "Failed to register MSI handler. Aborting.\n"); + if (rc) return -ENODEV; - } /* * Some (old and buggy?) hardware drops 64-bit addressed PCIe packets, diff --git a/drivers/comedi/drivers/aio_iiro_16.c b/drivers/comedi/drivers/aio_iiro_16.c index d5d18fa2c638..52385b14a9a4 100644 --- a/drivers/comedi/drivers/aio_iiro_16.c +++ b/drivers/comedi/drivers/aio_iiro_16.c @@ -59,6 +59,9 @@ static irqreturn_t aio_iiro_16_cos(int irq, void *d) unsigned int status; unsigned int val; + if (!dev->attached) + return IRQ_NONE; + status = inb(dev->iobase + AIO_IIRO_16_STATUS); if (!(status & AIO_IIRO_16_STATUS_IRQE)) return IRQ_NONE; diff --git a/drivers/comedi/drivers/c6xdigio.c b/drivers/comedi/drivers/c6xdigio.c index b6563a48ada6..fd36f820979c 100644 --- a/drivers/comedi/drivers/c6xdigio.c +++ b/drivers/comedi/drivers/c6xdigio.c @@ -224,10 +224,10 @@ static void c6xdigio_init(struct comedi_device *dev) static const struct pnp_device_id c6xdigio_pnp_tbl[] = { /* Standard LPT Printer Port */ - {.id = "PNP0400", .driver_data = 0}, + { .id = "PNP0400" }, /* ECP Printer Port */ - {.id = "PNP0401", .driver_data = 0}, - {} + { .id = "PNP0401" }, + { } }; static struct pnp_driver c6xdigio_pnp_driver = { diff --git a/drivers/comedi/drivers/das6402.c b/drivers/comedi/drivers/das6402.c index 516a5d5a2840..384e9ba8d5df 100644 --- a/drivers/comedi/drivers/das6402.c +++ b/drivers/comedi/drivers/das6402.c @@ -173,10 +173,16 @@ static irqreturn_t das6402_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; + struct comedi_async *async; + struct comedi_cmd *cmd; unsigned int status; + if (!dev->attached) + return IRQ_NONE; + + async = s->async; + cmd = &async->cmd; + status = inb(dev->iobase + DAS6402_STATUS_REG); if ((status & DAS6402_STATUS_INT) == 0) return IRQ_NONE; diff --git a/drivers/comedi/drivers/dt2811.c b/drivers/comedi/drivers/dt2811.c index bcc4b5ef48e8..0438b8c90e44 100644 --- a/drivers/comedi/drivers/dt2811.c +++ b/drivers/comedi/drivers/dt2811.c @@ -193,13 +193,16 @@ static irqreturn_t dt2811_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; + struct comedi_async *async; + struct comedi_cmd *cmd; unsigned int status; if (!dev->attached) return IRQ_NONE; + async = s->async; + cmd = &async->cmd; + status = inb(dev->iobase + DT2811_ADCSR_REG); if (status & DT2811_ADCSR_ADERROR) { diff --git a/drivers/comedi/drivers/ni_at_a2150.c b/drivers/comedi/drivers/ni_at_a2150.c index 44221c928e32..86629b495fed 100644 --- a/drivers/comedi/drivers/ni_at_a2150.c +++ b/drivers/comedi/drivers/ni_at_a2150.c @@ -132,11 +132,11 @@ static irqreturn_t a2150_interrupt(int irq, void *d) struct comedi_device *dev = d; struct a2150_private *devpriv = dev->private; struct comedi_isadma *dma = devpriv->dma; - struct comedi_isadma_desc *desc = &dma->desc[0]; + struct comedi_isadma_desc *desc; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - unsigned short *buf = desc->virt_addr; + struct comedi_async *async; + struct comedi_cmd *cmd; + unsigned short *buf; unsigned int max_points, num_points, residue, leftover; unsigned short dpnt; int status; @@ -145,6 +145,11 @@ static irqreturn_t a2150_interrupt(int irq, void *d) if (!dev->attached) return IRQ_HANDLED; + desc = &dma->desc[0]; + async = s->async; + cmd = &async->cmd; + buf = desc->virt_addr; + status = inw(dev->iobase + STATUS_REG); if ((status & INTR_BIT) == 0) return IRQ_NONE; diff --git a/drivers/comedi/drivers/ni_atmio.c b/drivers/comedi/drivers/ni_atmio.c index 7bc336333ace..537301eee9bf 100644 --- a/drivers/comedi/drivers/ni_atmio.c +++ b/drivers/comedi/drivers/ni_atmio.c @@ -215,13 +215,13 @@ static const int ni_irqpin[] = { #include "ni_mio_common.c" -static const struct pnp_device_id __maybe_unused device_ids[] = { - {.id = "NIC1900", .driver_data = 0}, - {.id = "NIC2400", .driver_data = 0}, - {.id = "NIC2500", .driver_data = 0}, - {.id = "NIC2600", .driver_data = 0}, - {.id = "NIC2700", .driver_data = 0}, - {.id = ""} +static const struct pnp_device_id device_ids[] = { + { .id = "NIC1900" }, + { .id = "NIC2400" }, + { .id = "NIC2500" }, + { .id = "NIC2600" }, + { .id = "NIC2700" }, + { } }; MODULE_DEVICE_TABLE(pnp, device_ids); diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c index b2772d909896..6765cdc276ca 100644 --- a/drivers/comedi/drivers/ni_atmio16d.c +++ b/drivers/comedi/drivers/ni_atmio16d.c @@ -223,6 +223,9 @@ static irqreturn_t atmio16d_interrupt(int irq, void *d) struct comedi_subdevice *s = dev->read_subdev; unsigned short val; + if (!dev->attached) + return IRQ_NONE; + val = inw(dev->iobase + AD_FIFO_REG); comedi_buf_write_samples(s, &val, 1); comedi_handle_events(dev, s); diff --git a/drivers/comedi/drivers/ni_pcimio.c b/drivers/comedi/drivers/ni_pcimio.c index 4be9ca4f4828..299e2a0e9474 100644 --- a/drivers/comedi/drivers/ni_pcimio.c +++ b/drivers/comedi/drivers/ni_pcimio.c @@ -685,17 +685,18 @@ static const struct ni_board_struct ni_boards[] = { .name = "pci-6220", .n_adchan = 16, .ai_maxdata = 0xffff, - .ai_fifo_depth = 512, /* FIXME: guess */ + .ai_fifo_depth = 4095, .gainlkup = ai_gain_622x, .ai_speed = 4000, .reg_type = ni_reg_622x, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PXI6220] = { .name = "pxi-6220", .n_adchan = 16, .ai_maxdata = 0xffff, - .ai_fifo_depth = 512, /* FIXME: guess */ + .ai_fifo_depth = 4095, .gainlkup = ai_gain_622x, .ai_speed = 4000, .reg_type = ni_reg_622x, diff --git a/drivers/comedi/drivers/pcl711.c b/drivers/comedi/drivers/pcl711.c index 5d2c4b2aa3bb..8ad5789966f0 100644 --- a/drivers/comedi/drivers/pcl711.c +++ b/drivers/comedi/drivers/pcl711.c @@ -184,7 +184,7 @@ static irqreturn_t pcl711_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; + struct comedi_cmd *cmd; unsigned short data; if (!dev->attached) { @@ -192,6 +192,7 @@ static irqreturn_t pcl711_interrupt(int irq, void *d) return IRQ_HANDLED; } + cmd = &s->async->cmd; data = pcl711_ai_get_sample(dev, s); outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG); diff --git a/drivers/comedi/drivers/pcl816.c b/drivers/comedi/drivers/pcl816.c index 1fcb2f798c7a..b0d30cb1cfe9 100644 --- a/drivers/comedi/drivers/pcl816.c +++ b/drivers/comedi/drivers/pcl816.c @@ -242,7 +242,7 @@ static irqreturn_t pcl816_interrupt(int irq, void *d) struct comedi_subdevice *s = dev->read_subdev; struct pcl816_private *devpriv = dev->private; struct comedi_isadma *dma = devpriv->dma; - struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; + struct comedi_isadma_desc *desc; unsigned int nsamples; unsigned int bufptr; @@ -257,6 +257,7 @@ static irqreturn_t pcl816_interrupt(int irq, void *d) return IRQ_HANDLED; } + desc = &dma->desc[dma->cur_dma]; nsamples = comedi_bytes_to_samples(s, desc->size) - devpriv->ai_poll_ptr; bufptr = devpriv->ai_poll_ptr; diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c index aa775a024fc7..89f32c493caa 100644 --- a/drivers/comedi/drivers/pcl818.c +++ b/drivers/comedi/drivers/pcl818.c @@ -534,13 +534,14 @@ static irqreturn_t pcl818_interrupt(int irq, void *d) struct comedi_device *dev = d; struct pcl818_private *devpriv = dev->private; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; + struct comedi_cmd *cmd; if (!dev->attached || !devpriv->ai_cmd_running) { pcl818_ai_clear_eoc(dev); return IRQ_HANDLED; } + cmd = &s->async->cmd; if (devpriv->ai_cmd_canceled) { /* * The cleanup from ai_cancel() has been delayed diff --git a/drivers/comedi/drivers/pcmmio.c b/drivers/comedi/drivers/pcmmio.c index d38202c8a12b..f42b7343b4e4 100644 --- a/drivers/comedi/drivers/pcmmio.c +++ b/drivers/comedi/drivers/pcmmio.c @@ -362,6 +362,9 @@ static irqreturn_t interrupt_pcmmio(int irq, void *d) unsigned int triggered; unsigned char int_pend; + if (!dev->attached) + return IRQ_NONE; + /* are there any interrupts pending */ int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07; if (!int_pend) diff --git a/drivers/comedi/drivers/pcmuio.c b/drivers/comedi/drivers/pcmuio.c index 0995911a3ea3..d9995cbeecb6 100644 --- a/drivers/comedi/drivers/pcmuio.c +++ b/drivers/comedi/drivers/pcmuio.c @@ -362,12 +362,15 @@ static irqreturn_t pcmuio_interrupt(int irq, void *d) struct pcmuio_private *devpriv = dev->private; int handled = 0; + if (!dev->attached) + return IRQ_NONE; + if (irq == dev->irq) handled += pcmuio_handle_asic_interrupt(dev, 0); if (irq == devpriv->irq2) handled += pcmuio_handle_asic_interrupt(dev, 1); - return handled ? IRQ_HANDLED : IRQ_NONE; + return IRQ_RETVAL(handled); } /* chip->spinlock is already locked */ diff --git a/drivers/comedi/drivers/quatech_daqp_cs.c b/drivers/comedi/drivers/quatech_daqp_cs.c index 2a76c75c513b..d5e3e213e233 100644 --- a/drivers/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/comedi/drivers/quatech_daqp_cs.c @@ -211,13 +211,15 @@ static irqreturn_t daqp_interrupt(int irq, void *dev_id) { struct comedi_device *dev = dev_id; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; + struct comedi_cmd *cmd; int loop_limit = 10000; int status; if (!dev->attached) return IRQ_NONE; + cmd = &s->async->cmd; + status = inb(dev->iobase + DAQP_STATUS_REG); if (!(status & DAQP_STATUS_EVENTS)) return IRQ_NONE; diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index a3d8f7a5874e..9eee281350d0 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -759,11 +759,8 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) /* All events reported through the global interrupt */ ret = devm_request_irq(&pdev->dev, ddata->irq[0], stm32_timer_cnt_isr, 0, dev_name(dev), counter); - if (ret) { - dev_err(dev, "Failed to request irq %d (err %d)\n", - ddata->irq[0], ret); + if (ret) return ret; - } } else { for (i = 0; i < priv->nr_irqs; i++) { /* @@ -775,11 +772,8 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, ddata->irq[i], stm32_timer_cnt_isr, 0, dev_name(dev), counter); - if (ret) { - dev_err(dev, "Failed to request irq %d (err %d)\n", - ddata->irq[i], ret); + if (ret) return ret; - } } } diff --git a/drivers/counter/ti-ecap-capture.c b/drivers/counter/ti-ecap-capture.c index f69b6920463f..d77a256f0ed3 100644 --- a/drivers/counter/ti-ecap-capture.c +++ b/drivers/counter/ti-ecap-capture.c @@ -513,7 +513,7 @@ static int ecap_cnt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, ret, ecap_cnt_isr, 0, pdev->name, counter_dev); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; platform_set_drvdata(pdev, counter_dev); diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index d9302ec21163..0dda3f2b14c7 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -526,7 +526,7 @@ static int ti_eqep_probe(struct platform_device *pdev) err = devm_request_threaded_irq(dev, irq, NULL, ti_eqep_irq_handler, IRQF_ONESHOT, dev_name(dev), counter); if (err < 0) - return dev_err_probe(dev, err, "failed to request IRQ\n"); + return err; counter->name = dev_name(dev); counter->parent = dev; diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 79ab44fe26aa..73c4ba3dc0bc 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -12,6 +12,7 @@ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/ioport.h> #include <asm/io.h> @@ -393,7 +394,7 @@ static int __init eisa_probe(struct eisa_root_device *root) } } - dev_info(root->dev, "EISA: Detected %d card%s\n", c, c == 1 ? "" : "s"); + dev_info(root->dev, "EISA: Detected %d card%s\n", c, str_plural(c)); return 0; } diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 7e3c9f38297b..e735f43dcdeb 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -210,3 +210,4 @@ module_platform_driver(adc_jack_driver); MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); MODULE_DESCRIPTION("ADC Jack extcon driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index daddb5224794..b360f752d191 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -7,17 +7,28 @@ #include <linux/arm-smccc.h> #include <linux/bitfield.h> #include <linux/completion.h> +#include <linux/delay.h> +#include <linux/firmware/intel/stratix10-svc-client.h> +#include <linux/kernel.h> #include <linux/kobject.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/firmware/intel/stratix10-svc-client.h> #include <linux/string.h> #include <linux/sysfs.h> -#include <linux/delay.h> -#define RSU_ERASE_SIZE_MASK GENMASK_ULL(63, 32) +#define RSU_STATE_MASK GENMASK_ULL(31, 0) +#define RSU_VERSION_MASK GENMASK_ULL(63, 32) +#define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0) +#define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32) +/* + * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO packs each flash word as: + * [63:32] erase_size, [31:0] size (see stratix10-smc.h). + */ +#define RSU_DEVICE_INFO_SIZE_MASK GENMASK_ULL(31, 0) +#define RSU_DEVICE_INFO_ERASE_SIZE_MASK GENMASK_ULL(63, 32) + #define RSU_DCMF0_MASK GENMASK_ULL(31, 0) #define RSU_DCMF1_MASK GENMASK_ULL(63, 32) #define RSU_DCMF2_MASK GENMASK_ULL(31, 0) @@ -33,11 +44,32 @@ #define INVALID_DCMF_VERSION 0xFF #define INVALID_DCMF_STATUS 0xFFFFFFFF #define INVALID_SPT_ADDRESS 0x0 +#define INVALID_DEVICE_INFO (~0U) #define RSU_RETRY_SLEEP_MS (1U) #define RSU_ASYNC_MSG_RETRY (3U) +#define RSU_GET_SPT_CMD 0x5A #define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int)) +struct flash_device_info { + unsigned int size; + unsigned int erase_size; +}; + +/** + * rsu_device_info_set_from_packed() - Decode one RSU device-info SMC word + * @di: slot to fill + * @packed: register value: [63:32] erase_size, [31:0] size + * (INTEL_SIP_SMC_RSU_GET_DEVICE_INFO) + */ +static void rsu_device_info_set_from_packed(struct flash_device_info *di, + unsigned long packed) +{ + di->size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_SIZE_MASK, packed); + di->erase_size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_ERASE_SIZE_MASK, + packed); +} + typedef void (*rsu_callback)(struct stratix10_svc_client *client, struct stratix10_svc_cb_data *data); /** @@ -46,6 +78,7 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @client: active service client * @completion: state for callback completion * @lock: a mutex to protect callback completion state + * @async: supports async operations * @status.current_image: address of image currently running in flash * @status.fail_image: address of failed image in flash * @status.version: the interface version number of RSU firmware @@ -60,16 +93,20 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @dcmf_status.dcmf1: dcmf1 status * @dcmf_status.dcmf2: dcmf2 status * @dcmf_status.dcmf3: dcmf3 status + * @device_info: per-device flash information array; each entry contains + * size and erase size for one flash device * @retry_counter: the current image's retry counter * @max_retry: the preset max retry value * @spt0_address: address of spt0 * @spt1_address: address of spt1 + * @get_spt_response_buf: response from sdm for get_spt command */ struct stratix10_rsu_priv { struct stratix10_svc_chan *chan; struct stratix10_svc_client client; struct completion completion; struct mutex lock; + bool async; struct { unsigned long current_image; unsigned long fail_image; @@ -93,17 +130,74 @@ struct stratix10_rsu_priv { unsigned int dcmf3; } dcmf_status; + struct flash_device_info device_info[4]; + unsigned int retry_counter; unsigned int max_retry; unsigned long spt0_address; unsigned long spt1_address; + + unsigned int *get_spt_response_buf; }; +/** + * rsu_device_info_invalidate() - Mark all cached QSPI device slots invalid + * @priv: RSU private data + */ +static void rsu_device_info_invalidate(struct stratix10_rsu_priv *priv) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(priv->device_info); i++) { + priv->device_info[i].size = INVALID_DEVICE_INFO; + priv->device_info[i].erase_size = INVALID_DEVICE_INFO; + } +} + typedef void (*rsu_async_callback)(struct device *dev, struct stratix10_rsu_priv *priv, struct stratix10_svc_cb_data *data); /** + * rsu_status_callback() - Status callback from Intel Service Layer + * @client: pointer to service client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for RSU status request. Status is + * only updated after a system reboot, so a get updated status call is + * made during driver probe. + */ +static void rsu_status_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) { + priv->status.version = FIELD_GET(RSU_VERSION_MASK, + res->a2); + priv->status.state = FIELD_GET(RSU_STATE_MASK, res->a2); + priv->status.fail_image = res->a1; + priv->status.current_image = res->a0; + priv->status.error_location = + FIELD_GET(RSU_ERROR_LOCATION_MASK, res->a3); + priv->status.error_details = + FIELD_GET(RSU_ERROR_DETAIL_MASK, res->a3); + } else { + dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n", + res->a0); + priv->status.version = 0; + priv->status.state = 0; + priv->status.fail_image = 0; + priv->status.current_image = 0; + priv->status.error_location = 0; + priv->status.error_details = 0; + } + + complete(&priv->completion); +} + +/** * rsu_async_status_callback() - Status callback from rsu_async_send() * @dev: pointer to device object * @priv: pointer to priv object @@ -147,6 +241,32 @@ static void rsu_command_callback(struct stratix10_svc_client *client, complete(&priv->completion); } +/** + * rsu_retry_callback() - Callback from Intel service layer for getting + * the current image's retry counter from the firmware + * @client: pointer to client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for retry counter, which is used by + * user to know how many times the images is still allowed to reload + * itself before giving up and starting RSU fail-over flow. + */ +static void rsu_retry_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + unsigned int *counter = (unsigned int *)data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) + priv->retry_counter = *counter; + else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) + dev_warn(client->dev, "Secure FW doesn't support retry\n"); + else + dev_err(client->dev, "Failed to get retry counter %lu\n", + BIT(data->status)); + + complete(&priv->completion); +} /** * rsu_max_retry_callback() - Callback from Intel service layer for getting @@ -229,8 +349,57 @@ static void rsu_dcmf_status_callback(struct stratix10_svc_client *client, } /** - * rsu_async_get_spt_table_callback() - Callback to be used by the rsu_async_send() - * to retrieve the SPT table information. + * rsu_get_device_info_callback() - Callback from Intel service layer for + * getting the QSPI device info + * @client: pointer to client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for QSPI device info. + * @data->kaddr1 points to struct arm_smccc_1_2_regs on SVC_STATUS_OK or + * SVC_STATUS_ERROR; it is NULL on SVC_STATUS_NO_SUPPORT (unsupported command). + */ +static void rsu_get_device_info_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + struct arm_smccc_1_2_regs *res = data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) { + if (!res) { + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO: missing result payload\n"); + rsu_device_info_invalidate(priv); + complete(&priv->completion); + return; + } + + rsu_device_info_set_from_packed(&priv->device_info[0], res->a1); + rsu_device_info_set_from_packed(&priv->device_info[1], res->a2); + rsu_device_info_set_from_packed(&priv->device_info[2], res->a3); + rsu_device_info_set_from_packed(&priv->device_info[3], res->a4); + + } else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) { + dev_warn(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO not supported by firmware\n"); + rsu_device_info_invalidate(priv); + } else { + if (res) + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO returned 0x%lX\n", + res->a0); + else + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO failed with status 0x%X\n", + data->status); + rsu_device_info_invalidate(priv); + } + + complete(&priv->completion); +} + +/** + * rsu_async_get_spt_table_callback() - Callback to be used by the + * rsu_async_send() to retrieve the SPT table information. * @dev: pointer to device object * @priv: pointer to priv object * @data: pointer to callback data structure @@ -243,6 +412,38 @@ static void rsu_async_get_spt_table_callback(struct device *dev, priv->spt1_address = *((unsigned long *)data->kaddr2); } +static void rsu_get_spt_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + unsigned long *mbox_err = (unsigned long *)data->kaddr1; + unsigned long *resp_len = (unsigned long *)data->kaddr2; + + if (data->status != BIT(SVC_STATUS_OK) || (*mbox_err) || + (*resp_len != RSU_GET_SPT_RESP_LEN)) + goto error; + + priv->spt0_address = priv->get_spt_response_buf[0]; + priv->spt0_address <<= 32; + priv->spt0_address |= priv->get_spt_response_buf[1]; + priv->spt1_address = priv->get_spt_response_buf[2]; + priv->spt1_address <<= 32; + priv->spt1_address |= priv->get_spt_response_buf[3]; + + goto complete; + +error: + dev_err(priv->client.dev, + "failed to get SPTs (status=%#x, mbox_err=%lu, resp_len=%lu)\n", + data->status, mbox_err ? *mbox_err : 0, + resp_len ? *resp_len : 0); + +complete: + stratix10_svc_free_memory(priv->chan, priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + complete(&priv->completion); +} + /** * __rsu_send_msg_locked() - send a message to Intel service layer * @priv: pointer to rsu private data @@ -259,7 +460,7 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv, unsigned long arg, rsu_callback callback) { - struct stratix10_svc_client_msg msg; + struct stratix10_svc_client_msg msg = {0}; int ret; lockdep_assert_held(&priv->lock); @@ -271,6 +472,14 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv, if (arg) msg.arg[0] = arg; + if (command == COMMAND_MBOX_SEND_CMD) { + msg.arg[1] = 0; + msg.payload = NULL; + msg.payload_length = 0; + msg.payload_output = priv->get_spt_response_buf; + msg.payload_length_output = RSU_GET_SPT_RESP_LEN; + } + ret = stratix10_svc_send(priv->chan, &msg); if (ret < 0) goto status_done; @@ -395,6 +604,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri if (status && !handle) { dev_err(dev, "Failed to send async message\n"); + if (msg.payload_output) + stratix10_svc_free_memory(priv->chan, msg.payload_output); return -ETIMEDOUT; } @@ -434,6 +645,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri } status_done: + if (msg.payload_output) + stratix10_svc_free_memory(priv->chan, msg.payload_output); stratix10_svc_async_done(priv->chan, handle); return ret; } @@ -681,15 +894,30 @@ static ssize_t notify_store(struct device *dev, if (ret) return ret; - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL); + if (priv->async) + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL); + else + ret = rsu_send_msg(priv, COMMAND_RSU_NOTIFY, status, rsu_command_callback); if (ret) { dev_err(dev, "Error, RSU notify returned %i\n", ret); return ret; } /* to get the updated state */ - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, - rsu_async_status_callback); + if (priv->async) { + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, + rsu_async_status_callback); + } else { + ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0, + rsu_status_callback); + /* + * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS; + * issue it separately only in synchronous mode. + */ + if (!ret) + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, + rsu_retry_callback); + } if (ret) { dev_err(dev, "Error, getting RSU status %i\n", ret); return ret; @@ -698,6 +926,75 @@ static ssize_t notify_store(struct device *dev, return count; } +static ssize_t rsu_device_info_show(struct device *dev, char *buf, + unsigned int index, bool erase_size) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + unsigned int value; + + if (!priv) + return -ENODEV; + + if (index >= ARRAY_SIZE(priv->device_info)) + return -EINVAL; + + value = erase_size ? priv->device_info[index].erase_size : + priv->device_info[index].size; + + if (value == INVALID_DEVICE_INFO) + return -EIO; + + return sysfs_emit(buf, "0x%08x\n", value); +} + +static ssize_t size0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 0, false); +} + +static ssize_t size1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 1, false); +} + +static ssize_t size2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 2, false); +} + +static ssize_t size3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 3, false); +} + +static ssize_t erase_size0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 0, true); +} + +static ssize_t erase_size1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 1, true); +} + +static ssize_t erase_size2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 2, true); +} + +static ssize_t erase_size3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 3, true); +} + static ssize_t spt0_address_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -742,6 +1039,14 @@ static DEVICE_ATTR_RO(dcmf0_status); static DEVICE_ATTR_RO(dcmf1_status); static DEVICE_ATTR_RO(dcmf2_status); static DEVICE_ATTR_RO(dcmf3_status); +static DEVICE_ATTR_RO(size0); +static DEVICE_ATTR_RO(size1); +static DEVICE_ATTR_RO(size2); +static DEVICE_ATTR_RO(size3); +static DEVICE_ATTR_RO(erase_size0); +static DEVICE_ATTR_RO(erase_size1); +static DEVICE_ATTR_RO(erase_size2); +static DEVICE_ATTR_RO(erase_size3); static DEVICE_ATTR_WO(reboot_image); static DEVICE_ATTR_WO(notify); static DEVICE_ATTR_RO(spt0_address); @@ -764,6 +1069,14 @@ static struct attribute *rsu_attrs[] = { &dev_attr_dcmf1_status.attr, &dev_attr_dcmf2_status.attr, &dev_attr_dcmf3_status.attr, + &dev_attr_size0.attr, + &dev_attr_size1.attr, + &dev_attr_size2.attr, + &dev_attr_size3.attr, + &dev_attr_erase_size0.attr, + &dev_attr_erase_size1.attr, + &dev_attr_erase_size2.attr, + &dev_attr_erase_size3.attr, &dev_attr_reboot_image.attr, &dev_attr_notify.attr, &dev_attr_spt0_address.attr, @@ -795,7 +1108,12 @@ static int stratix10_rsu_probe(struct platform_device *pdev) priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; - /* spt0/1_address and status fields default to 0 from kzalloc */ + priv->max_retry = INVALID_RETRY_COUNTER; + priv->spt0_address = INVALID_SPT_ADDRESS; + priv->spt1_address = INVALID_SPT_ADDRESS; + priv->get_spt_response_buf = NULL; + priv->async = false; + rsu_device_info_invalidate(priv); mutex_init(&priv->lock); init_completion(&priv->completion); @@ -809,19 +1127,39 @@ static int stratix10_rsu_probe(struct platform_device *pdev) } ret = stratix10_svc_add_async_client(priv->chan, false); - if (ret) { - dev_err(dev, "failed to add async client\n"); - goto free_channel; + if (ret < 0) { + dev_dbg(dev, "Async operations not supported, fallback to non-async mode\n"); + priv->async = false; + } else { + priv->async = true; } platform_set_drvdata(pdev, priv); /* get the initial state from firmware */ - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, - rsu_async_status_callback); + if (priv->async) + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, + rsu_async_status_callback); + else + ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0, + rsu_status_callback); if (ret) { dev_err(dev, "Error, getting RSU status %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; + } + + /* + * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS; + * issue it separately only in synchronous mode. + */ + if (!priv->async) { + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); + if (ret) { + dev_err(dev, "Error, getting RSU retry %i\n", ret); + goto free_channel; + } } /* get DCMF version from firmware */ @@ -829,28 +1167,64 @@ static int stratix10_rsu_probe(struct platform_device *pdev) rsu_dcmf_version_callback); if (ret) { dev_err(dev, "Error, getting DCMF version %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; } ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, 0, rsu_dcmf_status_callback); if (ret) { dev_err(dev, "Error, getting DCMF status %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; } ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0, rsu_max_retry_callback); if (ret) { dev_err(dev, "Error, getting RSU max retry %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; + } + + /* get QSPI device info from firmware */ + ret = rsu_send_msg(priv, COMMAND_RSU_GET_DEVICE_INFO, 0, + rsu_get_device_info_callback); + if (ret) { + dev_err(dev, "Error, getting QSPI Device Info %i\n", ret); + if (priv->async) + goto remove_async_client; + goto free_channel; } - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0, - rsu_async_get_spt_table_callback); + if (priv->async) { + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, + 0, rsu_async_get_spt_table_callback); + } else { + priv->get_spt_response_buf = + stratix10_svc_allocate_memory(priv->chan, RSU_GET_SPT_RESP_LEN); + if (IS_ERR(priv->get_spt_response_buf)) { + ret = PTR_ERR(priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + dev_err(dev, "failed to allocate get spt buffer\n"); + } else { + ret = rsu_send_msg(priv, COMMAND_MBOX_SEND_CMD, + RSU_GET_SPT_CMD, rsu_get_spt_callback); + } + } if (ret) { dev_err(dev, "Error, getting SPT table %i\n", ret); - goto remove_async_client; + if (priv->async) { + goto remove_async_client; + } else if (!IS_ERR_OR_NULL(priv->get_spt_response_buf)) { + stratix10_svc_free_memory(priv->chan, + priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + } + goto free_channel; } return 0; @@ -866,6 +1240,9 @@ static void stratix10_rsu_remove(struct platform_device *pdev) { struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev); + if (priv->async) + stratix10_svc_remove_async_client(priv->chan); + stratix10_svc_free_channel(priv->chan); } diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 5e20057ee344..f8c2da207cb4 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -20,6 +20,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/string.h> #include <linux/firmware/intel/stratix10-smc.h> #include <linux/firmware/intel/stratix10-svc-client.h> #include <linux/types.h> @@ -45,6 +46,7 @@ /* stratix10 service layer clients */ #define STRATIX10_RSU "stratix10-rsu" +#define SOCFPGA_HWMON "socfpga-hwmon" /* Maximum number of SDM client IDs. */ #define MAX_SDM_CLIENT_IDS 16 @@ -104,9 +106,11 @@ struct stratix10_svc_chan; /** * struct stratix10_svc - svc private data * @stratix10_svc_rsu: pointer to stratix10 RSU device + * @stratix10_svc_hwmon: pointer to stratix10 HWMON device */ struct stratix10_svc { struct platform_device *stratix10_svc_rsu; + struct platform_device *stratix10_svc_hwmon; }; /** @@ -445,13 +449,15 @@ static void svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl, * svc_thread_recv_status_ok() - handle the successful status * @p_data: pointer to service data structure * @cb_data: pointer to callback data structure to service client - * @res: result from SMC or HVC call + * @res: result from SMC or HVC call (a0-a3; used for routing and most commands) + * @res12: full v1.2 result for %COMMAND_RSU_GET_DEVICE_INFO, else NULL * * Send back the correspond status to the service clients. */ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, struct stratix10_svc_cb_data *cb_data, - struct arm_smccc_res res) + struct arm_smccc_res res, + struct arm_smccc_1_2_regs *res12) { cb_data->kaddr1 = NULL; cb_data->kaddr2 = NULL; @@ -513,6 +519,16 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, res.a2 = res.a2 * BYTE_TO_WORD_SIZE; cb_data->kaddr2 = &res.a2; break; + case COMMAND_RSU_GET_DEVICE_INFO: + if (WARN_ON(!res12)) { + cb_data->status = BIT(SVC_STATUS_ERROR); + break; + } + cb_data->status = BIT(SVC_STATUS_OK); + cb_data->kaddr1 = res12; + cb_data->kaddr2 = NULL; + cb_data->kaddr3 = NULL; + break; default: pr_warn("it shouldn't happen\n"); break; @@ -522,6 +538,10 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, p_data->chan->scl->receive_cb(p_data->chan->scl, cb_data); } +static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl, + const struct arm_smccc_1_2_regs *args, + struct arm_smccc_1_2_regs *res); + /** * svc_normal_to_secure_thread() - the function to run in the kthread * @data: data pointer for kthread function @@ -539,6 +559,7 @@ static int svc_normal_to_secure_thread(void *data) struct stratix10_svc_data *pdata = NULL; struct stratix10_svc_cb_data *cbdata = NULL; struct arm_smccc_res res; + struct arm_smccc_1_2_regs res12 = { 0 }; unsigned long a0, a1, a2, a3, a4, a5, a6, a7; int ret_fifo = 0; @@ -727,6 +748,16 @@ static int svc_normal_to_secure_thread(void *data) a5 = (unsigned long)pdata->paddr_output; a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE; break; + case COMMAND_RSU_GET_DEVICE_INFO: + a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO; + a1 = 0; + a2 = 0; + a3 = 0; + a4 = 0; + a5 = 0; + a6 = 0; + a7 = 0; + break; default: pr_warn("it shouldn't happen\n"); mutex_unlock(&ctrl->sdm_lock); @@ -740,7 +771,18 @@ static int svc_normal_to_secure_thread(void *data) pr_debug(" a3=0x%016x\n", (unsigned int)a3); pr_debug(" a4=0x%016x\n", (unsigned int)a4); pr_debug(" a5=0x%016x\n", (unsigned int)a5); - ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) { + struct arm_smccc_1_2_regs args12 = { 0 }; + + args12.a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO; + svc_smccc_1_2_full(ctrl, &args12, &res12); + res.a0 = res12.a0; + res.a1 = res12.a1; + res.a2 = res12.a2; + res.a3 = res12.a3; + } else { + ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + } pr_debug("%s: %s: after SMC call -- res.a0=0x%016x", __func__, chan->name, (unsigned int)res.a0); @@ -763,9 +805,15 @@ static int svc_normal_to_secure_thread(void *data) } switch (res.a0) { - case INTEL_SIP_SMC_STATUS_OK: - svc_thread_recv_status_ok(pdata, cbdata, res); + case INTEL_SIP_SMC_STATUS_OK: { + struct arm_smccc_1_2_regs *devinfo_res = + (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) ? + &res12 : NULL; + + svc_thread_recv_status_ok(pdata, cbdata, res, + devinfo_res); break; + } case INTEL_SIP_SMC_STATUS_BUSY: switch (pdata->command) { case COMMAND_RECONFIG_DATA_SUBMIT: @@ -806,10 +854,16 @@ static int svc_normal_to_secure_thread(void *data) case INTEL_SIP_SMC_RSU_ERROR: pr_err("%s: STATUS_ERROR\n", __func__); cbdata->status = BIT(SVC_STATUS_ERROR); - cbdata->kaddr1 = &res.a1; - cbdata->kaddr2 = (res.a2) ? - svc_pa_to_va(res.a2) : NULL; - cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL; + if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) { + cbdata->kaddr1 = &res12; + cbdata->kaddr2 = NULL; + cbdata->kaddr3 = NULL; + } else { + cbdata->kaddr1 = &res.a1; + cbdata->kaddr2 = (res.a2) ? + svc_pa_to_va(res.a2) : NULL; + cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL; + } pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata); break; default: @@ -1026,6 +1080,31 @@ static void svc_smccc_hvc(unsigned long a0, unsigned long a1, } /** + * svc_smccc_1_2_full() - SMC/HVC v1.2 call matching the sync channel method + * @ctrl: service controller (selects SMC vs HVC) + * @args: arguments + * @res: full register-file result (a0-a17) + */ +static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl, + const struct arm_smccc_1_2_regs *args, + struct arm_smccc_1_2_regs *res) +{ + if (ctrl->invoke_fn == svc_smccc_smc) { + arm_smccc_1_2_smc(args, res); + } else if (ctrl->invoke_fn == svc_smccc_hvc) { + arm_smccc_1_2_hvc(args, res); + } else { + WARN_ON_ONCE(1); + /* + * INTEL_SIP_SMC_STATUS_OK is 0; zero-filled res would be misrouted + * as success. Force an error path and clear fabricated payload. + */ + memset(res, 0, sizeof(*res)); + res->a0 = INTEL_SIP_SMC_STATUS_ERROR; + } +} + +/** * get_invoke_func() - invoke SMC or HVC call * @dev: pointer to device * @@ -1329,6 +1408,14 @@ int stratix10_svc_async_send(struct stratix10_svc_chan *chan, void *msg, args.a0 = INTEL_SIP_SMC_ASYNC_RSU_NOTIFY; args.a2 = p_msg->arg[0]; break; + case COMMAND_HWMON_READTEMP: + args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READTEMP; + args.a2 = p_msg->arg[0]; + break; + case COMMAND_HWMON_READVOLT: + args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READVOLT; + args.a2 = p_msg->arg[0]; + break; default: dev_err(ctrl->dev, "Invalid command ,%d\n", p_msg->command); ret = -EINVAL; @@ -1422,6 +1509,10 @@ static int stratix10_svc_async_prepare_response(struct stratix10_svc_chan *chan, */ data->kaddr1 = (void *)&handle->res; break; + case COMMAND_HWMON_READTEMP: + case COMMAND_HWMON_READVOLT: + data->kaddr1 = (void *)&handle->res.a2; + break; default: dev_alert(ctrl->dev, "Invalid command\n ,%d", p_msg->command); @@ -2016,16 +2107,38 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) if (ret) goto err_put_device; + if (IS_ENABLED(CONFIG_SENSORS_ALTERA_SOCFPGA_HWMON)) { + svc->stratix10_svc_hwmon = + platform_device_alloc(SOCFPGA_HWMON, 0); + if (!svc->stratix10_svc_hwmon) { + dev_err(dev, "failed to allocate %s device\n", + SOCFPGA_HWMON); + } else { + svc->stratix10_svc_hwmon->dev.parent = dev; + + ret = platform_device_add(svc->stratix10_svc_hwmon); + if (ret) { + dev_err(dev, "failed to add %s device: %d\n", + SOCFPGA_HWMON, ret); + platform_device_put(svc->stratix10_svc_hwmon); + svc->stratix10_svc_hwmon = NULL; + } + } + } + ret = of_platform_default_populate(dev_of_node(dev), NULL, dev); if (ret) - goto err_unregister_rsu_dev; + goto err_unregister_clients; pr_info("Intel Service Layer Driver Initialized\n"); return 0; -err_unregister_rsu_dev: - platform_device_unregister(svc->stratix10_svc_rsu); +err_unregister_clients: + if (svc->stratix10_svc_hwmon) + platform_device_unregister(svc->stratix10_svc_hwmon); + if (svc->stratix10_svc_rsu) + platform_device_unregister(svc->stratix10_svc_rsu); goto err_free_fifos; err_put_device: platform_device_put(svc->stratix10_svc_rsu); @@ -2050,6 +2163,8 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev) struct stratix10_svc *svc = ctrl->svc; platform_device_unregister(svc->stratix10_svc_rsu); + if (svc->stratix10_svc_hwmon) + platform_device_unregister(svc->stratix10_svc_hwmon); stratix10_svc_async_exit(ctrl); diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c index 44badfd11e1b..070c025ee215 100644 --- a/drivers/fpga/altera-cvp.c +++ b/drivers/fpga/altera-cvp.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/sizes.h> +#include <linux/string.h> #define CVP_BAR 0 /* BAR used for data transfer in memory mode */ #define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */ @@ -261,7 +262,7 @@ static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr, static int altera_cvp_send_block(struct altera_cvp_conf *conf, const u32 *data, size_t len) { - u32 mask, words = len / sizeof(u32); + u32 words = len / sizeof(u32); int i, remainder; for (i = 0; i < words; i++) @@ -270,9 +271,10 @@ static int altera_cvp_send_block(struct altera_cvp_conf *conf, /* write up to 3 trailing bytes, if any */ remainder = len % sizeof(u32); if (remainder) { - mask = BIT(remainder * 8) - 1; - if (mask) - conf->write_data(conf, *data & mask); + u32 word = 0; + + memcpy(&word, data, remainder); + conf->write_data(conf, word); } return 0; diff --git a/drivers/fpga/dfl-fme-perf.c b/drivers/fpga/dfl-fme-perf.c index 7aa4983ab67d..495edc448b68 100644 --- a/drivers/fpga/dfl-fme-perf.c +++ b/drivers/fpga/dfl-fme-perf.c @@ -925,6 +925,8 @@ static int fme_perf_pmu_register(struct platform_device *pdev, PERF_PMU_CAP_NO_EXCLUDE; name = devm_kasprintf(priv->dev, GFP_KERNEL, "dfl_fme%d", pdev->id); + if (!name) + return -ENOMEM; ret = perf_pmu_register(pmu, name, -1); if (ret) diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index 0a295ccf1644..b8ec2e6f615f 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -195,20 +195,18 @@ static int s10_ops_write_init(struct fpga_manager *mgr, ret = s10_svc_send_msg(priv, COMMAND_RECONFIG, &ctype, sizeof(ctype)); if (ret < 0) - goto init_done; + goto init_error; - ret = wait_for_completion_timeout( - &priv->status_return_completion, S10_RECONFIG_TIMEOUT); - if (!ret) { + if (!wait_for_completion_timeout(&priv->status_return_completion, + S10_RECONFIG_TIMEOUT)) { dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n"); ret = -ETIMEDOUT; - goto init_done; + goto init_error; } - ret = 0; if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) { ret = -ETIMEDOUT; - goto init_done; + goto init_error; } /* Allocate buffers from the service layer's pool. */ @@ -217,14 +215,16 @@ static int s10_ops_write_init(struct fpga_manager *mgr, if (IS_ERR(kbuf)) { s10_free_buffers(mgr); ret = PTR_ERR(kbuf); - goto init_done; + goto init_error; } priv->svc_bufs[i].buf = kbuf; priv->svc_bufs[i].lock = 0; } -init_done: + return 0; + +init_error: stratix10_svc_done(priv->chan); return ret; } @@ -342,6 +342,9 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf, if (!s10_free_buffers(mgr)) dev_err(dev, "%s not all buffers were freed\n", __func__); + if (ret < 0) + stratix10_svc_done(priv->chan); + return ret; } diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index 6994d68e9036..45b65a3264af 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -118,46 +118,30 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev) if (IS_ERR(priv->io_base)) return PTR_ERR(priv->io_base); - priv->clk = devm_clk_get(&pdev->dev, "aclk"); + priv->clk = devm_clk_get_prepared(&pdev->dev, "aclk"); if (IS_ERR(priv->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), "input clock not found\n"); - err = clk_prepare_enable(priv->clk); - if (err) { - dev_err(&pdev->dev, "unable to enable clock\n"); - return err; - } - - clk_disable(priv->clk); - br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name, &xlnx_pr_decoupler_br_ops, priv); if (IS_ERR(br)) { err = PTR_ERR(br); dev_err(&pdev->dev, "unable to register %s", priv->ipconfig->name); - goto err_clk; + return err; } platform_set_drvdata(pdev, br); return 0; - -err_clk: - clk_unprepare(priv->clk); - - return err; } static void xlnx_pr_decoupler_remove(struct platform_device *pdev) { struct fpga_bridge *bridge = platform_get_drvdata(pdev); - struct xlnx_pr_decoupler_data *p = bridge->priv; fpga_bridge_unregister(bridge); - - clk_unprepare(p->clk); } static struct platform_driver xlnx_pr_decoupler_driver = { diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 9d1d599ef718..2f8ba32e4002 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -598,7 +598,6 @@ static int zynq_fpga_probe(struct platform_device *pdev) err = devm_request_irq(dev, priv->irq, zynq_fpga_isr, 0, dev_name(dev), priv); if (err) { - dev_err(dev, "unable to request IRQ\n"); clk_disable_unprepare(priv->clk); return err; } diff --git a/drivers/gpib/agilent_82350b/agilent_82350b.c b/drivers/gpib/agilent_82350b/agilent_82350b.c index 9787c09faad8..490d44ad1b50 100644 --- a/drivers/gpib/agilent_82350b/agilent_82350b.c +++ b/drivers/gpib/agilent_82350b/agilent_82350b.c @@ -839,11 +839,10 @@ static int agilent_82350b_pci_probe(struct pci_dev *dev, const struct pci_device } static const struct pci_device_id agilent_82350b_pci_table[] = { - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, - PCI_SUBDEVICE_ID_82350A, 0, 0, 0 }, - { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82350B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82351A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, PCI_SUBDEVICE_ID_82350A) }, + { PCI_VDEVICE(AGILENT, PCI_DEVICE_ID_82350B) }, + { PCI_VDEVICE(AGILENT, PCI_DEVICE_ID_82351A) }, + { } }; MODULE_DEVICE_TABLE(pci, agilent_82350b_pci_table); diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c index 6dd8637c5964..c62c30fc3472 100644 --- a/drivers/gpib/cb7210/cb7210.c +++ b/drivers/gpib/cb7210/cb7210.c @@ -1093,10 +1093,10 @@ static int cb7210_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id cb7210_pci_table[] = { - {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - {PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } + { PCI_VDEVICE(CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB) }, + { PCI_VDEVICE(CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB) }, + { PCI_VDEVICE(QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB) }, + { } }; MODULE_DEVICE_TABLE(pci, cb7210_pci_table); diff --git a/drivers/gpib/cec/cec_gpib.c b/drivers/gpib/cec/cec_gpib.c index c13bc302d9e9..530a777b1384 100644 --- a/drivers/gpib/cec/cec_gpib.c +++ b/drivers/gpib/cec/cec_gpib.c @@ -353,8 +353,8 @@ static int cec_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id cec_pci_table[] = { - {CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID, 0, 0, 0 }, - {0} + { PCI_DEVICE_SUB(CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID) }, + { } }; MODULE_DEVICE_TABLE(pci, cec_pci_table); diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index 69f6aa73ab9a..384800e6bf97 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -289,18 +289,19 @@ int autopoll_all_devices(struct gpib_board *board) dev_dbg(board->gpib_dev, "autopoll has board lock\n"); retval = serial_poll_all(board, serial_timeout); - if (retval < 0) { - mutex_unlock(&board->big_gpib_mutex); - mutex_unlock(&board->user_mutex); - return retval; + if (retval >= 0) { + dev_dbg(board->gpib_dev, "complete\n"); + /* + * need to wake wait queue in case someone is + * waiting on RQS + */ + wake_up_interruptible(&board->wait); } - dev_dbg(board->gpib_dev, "complete\n"); - /* - * need to wake wait queue in case someone is - * waiting on RQS - */ - wake_up_interruptible(&board->wait); + if (retval <= 0) { + atomic_set(&board->stuck_srq, 1); + set_bit(SRQI_NUM, &board->status); + } mutex_unlock(&board->big_gpib_mutex); mutex_unlock(&board->user_mutex); diff --git a/drivers/gpib/common/iblib.c b/drivers/gpib/common/iblib.c index b672dd6aad25..511e1d61c1fb 100644 --- a/drivers/gpib/common/iblib.c +++ b/drivers/gpib/common/iblib.c @@ -193,9 +193,6 @@ static int autospoll_thread(void *board_void) } if (retval <= 0) { dev_err(board->gpib_dev, "stuck SRQ\n"); - - atomic_set(&board->stuck_srq, 1); // XXX could be better - set_bit(SRQI_NUM, &board->status); } } return retval; diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.c b/drivers/gpib/fmh_gpib/fmh_gpib.c index fcafdc02ea2e..5e10e9353fed 100644 --- a/drivers/gpib/fmh_gpib/fmh_gpib.c +++ b/drivers/gpib/fmh_gpib/fmh_gpib.c @@ -1338,7 +1338,7 @@ static int fmh_gpib_init(struct fmh_priv *e_priv, struct gpib_board *board, int write_byte(nec_priv, AUX_LO_SPEED, AUXMR); nec7210_set_handshake_mode(board, nec_priv, handshake_mode); - /* Hueristically check if hardware supports fifo half full/empty interrupts */ + /* Heuristically check if hardware supports fifo half full/empty interrupts */ fifo_status_bits = fifos_read(e_priv, FIFO_CONTROL_STATUS_REG); e_priv->supports_fifo_interrupts = (fifo_status_bits & TX_FIFO_EMPTY) && (fifo_status_bits & TX_FIFO_HALF_EMPTY); diff --git a/drivers/gpib/hp_82341/hp_82341.c b/drivers/gpib/hp_82341/hp_82341.c index 46175ba2ac36..e98cf5aea99f 100644 --- a/drivers/gpib/hp_82341/hp_82341.c +++ b/drivers/gpib/hp_82341/hp_82341.c @@ -803,8 +803,8 @@ static void hp_82341_detach(struct gpib_board *board) #if 0 /* unused, will be needed when the driver is turned into a pnp_driver */ static const struct pnp_device_id hp_82341_pnp_table[] = { - {.id = "HWP1411"}, - {.id = ""} + { .id = "HWP1411" }, + { } }; MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table); #endif diff --git a/drivers/gpib/include/amccs5933.h b/drivers/gpib/include/amccs5933.h index d7f63c795096..f8a80bdc70dc 100644 --- a/drivers/gpib/include/amccs5933.h +++ b/drivers/gpib/include/amccs5933.h @@ -13,7 +13,7 @@ enum { }; // incoming mailbox 0-3 register offsets -extern inline int INCOMING_MAILBOX_REG(unsigned int mailbox) +static inline int INCOMING_MAILBOX_REG(unsigned int mailbox) { return (0x10 + 4 * mailbox); }; @@ -29,25 +29,25 @@ enum { }; // select byte 0 to 3 of incoming mailbox -extern inline int INBOX_BYTE_BITS(unsigned int byte) +static inline int INBOX_BYTE_BITS(unsigned int byte) { return (byte & 0x3) << 8; }; // select incoming mailbox 0 to 3 -extern inline int INBOX_SELECT_BITS(unsigned int mailbox) +static inline int INBOX_SELECT_BITS(unsigned int mailbox) { return (mailbox & 0x3) << 10; }; // select byte 0 to 3 of outgoing mailbox -extern inline int OUTBOX_BYTE_BITS(unsigned int byte) +static inline int OUTBOX_BYTE_BITS(unsigned int byte) { return (byte & 0x3); }; // select outgoing mailbox 0 to 3 -extern inline int OUTBOX_SELECT_BITS(unsigned int mailbox) +static inline int OUTBOX_SELECT_BITS(unsigned int mailbox) { return (mailbox & 0x3) << 2; }; diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c index 3562f3184c28..6cd6ff596fda 100644 --- a/drivers/gpib/ines/ines_gpib.c +++ b/drivers/gpib/ines/ines_gpib.c @@ -371,14 +371,19 @@ enum ines_pci_subdevice_ids { }; static struct pci_device_id ines_pci_table[] = { - {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX, - PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, - {PCI_VENDOR_ID_AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, PCI_VENDOR_ID_AMCC, - PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, - {PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, - PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, 0, 0, 0}, - {PCI_DEVICE(PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB)}, - {0} + { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_INES_GPIB), + }, { + PCI_VDEVICE_SUB(AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, + PCI_VENDOR_ID_AMCC, PCI_SUBDEVICE_ID_INES_GPIB), + }, { + PCI_VDEVICE_SUB(INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, + PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030), + }, { + PCI_VDEVICE(QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB), + }, + { } }; MODULE_DEVICE_TABLE(pci, ines_pci_table); diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c index e6ea9422d6f2..dfb8f3ebc27f 100644 --- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c +++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -68,7 +68,7 @@ MODULE_DEVICE_TABLE(usb, lpvo_table); * (about twice the log volume of [1]) * To switch debug level: * At module loading: modprobe lpvo_usb_gpib debug={0,1,2} - * On the fly: echo {0,1,2} > /sys/modules/lpvo_usb_gpib/parameters/debug + * On the fly: echo {0,1,2} > /sys/module/lpvo_usb_gpib/parameters/debug */ static int debug; @@ -1811,14 +1811,9 @@ static ssize_t lpvo_write(struct file *file, const char __user *user_buffer, dev = file->private_data; - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, user_buffer, count)) { - kfree(buf); - return -EFAULT; - } + buf = memdup_user(user_buffer, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); rv = lpvo_do_write(dev, buf, count); kfree(buf); diff --git a/drivers/gpib/tnt4882/mite.h b/drivers/gpib/tnt4882/mite.h index a1fdba9672a0..dd251afa90e3 100644 --- a/drivers/gpib/tnt4882/mite.h +++ b/drivers/gpib/tnt4882/mite.h @@ -45,12 +45,12 @@ struct mite_struct { extern struct mite_struct *mite_devices; -extern inline unsigned int mite_irq(struct mite_struct *mite) +static inline unsigned int mite_irq(struct mite_struct *mite) { return mite->pcidev->irq; }; -extern inline unsigned int mite_device_id(struct mite_struct *mite) +static inline unsigned int mite_device_id(struct mite_struct *mite) { return mite->pcidev->device; }; diff --git a/drivers/gpib/tnt4882/tnt4882_gpib.c b/drivers/gpib/tnt4882/tnt4882_gpib.c index 51a920e1d9a4..3cd13f637ed4 100644 --- a/drivers/gpib/tnt4882/tnt4882_gpib.c +++ b/drivers/gpib/tnt4882/tnt4882_gpib.c @@ -1373,8 +1373,8 @@ static struct pci_driver tnt4882_pci_driver = { #if 0 /* unused, will be needed when the driver is turned into a pnp_driver */ static const struct pnp_device_id tnt4882_pnp_table[] = { - {.id = "NICC601"}, - {.id = ""} + { .id = "NICC601" }, + { } }; MODULE_DEVICE_TABLE(pnp, tnt4882_pnp_table); #endif diff --git a/drivers/greybus/manifest.c b/drivers/greybus/manifest.c index 9be5d95da587..3b685ace2036 100644 --- a/drivers/greybus/manifest.c +++ b/drivers/greybus/manifest.c @@ -122,6 +122,8 @@ static int identify_descriptor(struct gb_interface *intf, switch (desc_header->type) { case GREYBUS_TYPE_STRING: expected_size += sizeof(struct greybus_descriptor_string); + if (desc_size < expected_size) + break; expected_size += desc->string.length; /* String descriptors are padded to 4 byte boundaries */ diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 942282614c48..fecff8610ea8 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1993,7 +1993,7 @@ config SENSORS_SL28CPLD config SENSORS_SBTSI tristate "Emulated SB-TSI temperature sensor" - depends on I2C + depends on AMD_SBTSI help If you say yes here you get support for emulated temperature sensors on AMD SoCs with SB-TSI interface connected to a BMC device. @@ -2189,6 +2189,16 @@ config SENSORS_SMSC47M192 This driver can also be built as a module. If so, the module will be called smsc47m192. +config SENSORS_ALTERA_SOCFPGA_HWMON + tristate "Altera SoC FPGA hardware monitoring features" + depends on INTEL_STRATIX10_SERVICE + help + If you say yes here you get support for the temperature and + voltage sensors of Altera SoC FPGA devices. + + This driver can also be built as a module. If so, the module + will be called socfpga-hwmon. + config SENSORS_SMSC47B397 tristate "SMSC LPC47B397-NC" depends on HAS_IOPORT diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 0cad7e21634c..1229b6b3996d 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -224,6 +224,7 @@ obj-$(CONFIG_SENSORS_SMPRO) += smpro-hwmon.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o +obj-$(CONFIG_SENSORS_ALTERA_SOCFPGA_HWMON) += socfpga-hwmon.o obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o obj-$(CONFIG_SENSORS_STTS751) += stts751.o diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index fc17ad93a376..6502e20517a8 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -221,3 +221,4 @@ module_platform_driver(iio_hwmon_driver); MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); MODULE_DESCRIPTION("IIO to hwmon driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 1ac0288fbbd8..4fa7f522f245 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -748,3 +748,4 @@ MODULE_DESCRIPTION("NTC Thermistor Driver"); MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:ntc-thermistor"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index c28f8625cd3a..11c8108d69b2 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -7,13 +7,12 @@ * Copyright (c) 2020, Kun Yi <kunyi@google.com> */ +#include <linux/auxiliary_bus.h> #include <linux/err.h> -#include <linux/i2c.h> -#include <linux/init.h> #include <linux/hwmon.h> +#include <linux/init.h> #include <linux/module.h> -#include <linux/of.h> -#include <linux/bitfield.h> +#include <linux/misc/tsi.h> /* * SB-TSI registers only support SMBus byte data access. "_INT" registers are @@ -22,39 +21,17 @@ */ #define SBTSI_REG_TEMP_INT 0x01 /* RO */ #define SBTSI_REG_STATUS 0x02 /* RO */ -#define SBTSI_REG_CONFIG 0x03 /* RO */ #define SBTSI_REG_TEMP_HIGH_INT 0x07 /* RW */ #define SBTSI_REG_TEMP_LOW_INT 0x08 /* RW */ #define SBTSI_REG_TEMP_DEC 0x10 /* RW */ #define SBTSI_REG_TEMP_HIGH_DEC 0x13 /* RW */ #define SBTSI_REG_TEMP_LOW_DEC 0x14 /* RW */ -/* - * Bit for reporting value with temperature measurement range. - * bit == 0: Use default temperature range (0C to 255.875C). - * bit == 1: Use extended temperature range (-49C to +206.875C). - */ -#define SBTSI_CONFIG_EXT_RANGE_SHIFT 2 -/* - * ReadOrder bit specifies the reading order of integer and decimal part of - * CPU temperature for atomic reads. If bit == 0, reading integer part triggers - * latching of the decimal part, so integer part should be read first. - * If bit == 1, read order should be reversed. - */ -#define SBTSI_CONFIG_READ_ORDER_SHIFT 5 - #define SBTSI_TEMP_EXT_RANGE_ADJ 49000 #define SBTSI_TEMP_MIN 0 #define SBTSI_TEMP_MAX 255875 -/* Each client has this additional data */ -struct sbtsi_data { - struct i2c_client *client; - bool ext_range_mode; - bool read_order; -}; - /* * From SB-TSI spec: CPU temperature readings and limit registers encode the * temperature in increments of 0.125 from 0 to 255.875. The "high byte" @@ -84,40 +61,79 @@ static inline void sbtsi_mc_to_reg(s32 temp, u8 *integer, u8 *decimal) *decimal = (temp & 0x7) << 5; } +/* + * Read integer and decimal parts of an SB-TSI temperature register pair + * The read order is determined by the ReadOrder bit to ensure atomic latching. + */ +static int sbtsi_temp_read(struct sbtsi_data *data, u8 reg1, u8 reg2, + u8 *val1, u8 *val2) +{ + int ret; + + guard(sbtsi)(data); + ret = sbtsi_xfer(data, reg1, val1, true); + if (!ret) + ret = sbtsi_xfer(data, reg2, val2, true); + return ret; +} + +/* + * Write integer and decimal parts of an SB-TSI temperature register pair. + */ +static int sbtsi_temp_write(struct sbtsi_data *data, u8 reg_int, u8 reg_dec, + u8 val_int, u8 val_dec) +{ + int ret; + + guard(sbtsi)(data); + ret = sbtsi_xfer(data, reg_int, &val_int, false); + if (!ret) + ret = sbtsi_xfer(data, reg_dec, &val_dec, false); + return ret; +} + static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct sbtsi_data *data = dev_get_drvdata(dev); s32 temp_int, temp_dec; + int err; + u8 val_int, val_dec; switch (attr) { case hwmon_temp_input: - if (data->read_order) { - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); - } else { - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); - } + if (data->read_order) + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_DEC, SBTSI_REG_TEMP_INT, + &val_dec, &val_int); + else + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_INT, SBTSI_REG_TEMP_DEC, + &val_int, &val_dec); + if (err < 0) + return err; break; case hwmon_temp_max: - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT); - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC); + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_HIGH_INT, SBTSI_REG_TEMP_HIGH_DEC, + &val_int, &val_dec); + if (err < 0) + return err; break; case hwmon_temp_min: - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT); - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC); + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_LOW_INT, SBTSI_REG_TEMP_LOW_DEC, + &val_int, &val_dec); + + if (err < 0) + return err; break; default: return -EINVAL; } - - if (temp_int < 0) - return temp_int; - if (temp_dec < 0) - return temp_dec; - + temp_int = val_int; + temp_dec = val_dec; *val = sbtsi_reg_to_mc(temp_int, temp_dec); if (data->ext_range_mode) *val -= SBTSI_TEMP_EXT_RANGE_ADJ; @@ -129,7 +145,7 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct sbtsi_data *data = dev_get_drvdata(dev); - int reg_int, reg_dec, err; + int reg_int, reg_dec; u8 temp_int, temp_dec; switch (attr) { @@ -150,11 +166,7 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX); sbtsi_mc_to_reg(val, &temp_int, &temp_dec); - err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int); - if (err) - return err; - - return i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec); + return sbtsi_temp_write(data, reg_int, reg_dec, temp_int, temp_dec); } static umode_t sbtsi_is_visible(const void *data, @@ -195,55 +207,33 @@ static const struct hwmon_chip_info sbtsi_chip_info = { .info = sbtsi_info, }; -static int sbtsi_probe(struct i2c_client *client) +static int sbtsi_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) { - struct device *dev = &client->dev; + struct sbtsi_data *data = dev_get_drvdata(adev->dev.parent); + struct device *dev = &adev->dev; struct device *hwmon_dev; - struct sbtsi_data *data; - int err; - - data = devm_kzalloc(dev, sizeof(struct sbtsi_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->client = client; - - err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG); - if (err < 0) - return err; - data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), err); - data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), err); - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbtsi", data, &sbtsi_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } -static const struct i2c_device_id sbtsi_id[] = { - { .name = "sbtsi" }, +static const struct auxiliary_device_id sbtsi_id[] = { + { .name = AMD_SBTSI_ADEV "." AMD_SBTSI_AUX_HWMON }, { } }; -MODULE_DEVICE_TABLE(i2c, sbtsi_id); +MODULE_DEVICE_TABLE(auxiliary, sbtsi_id); -static const struct of_device_id __maybe_unused sbtsi_of_match[] = { - { - .compatible = "amd,sbtsi", - }, - { }, -}; -MODULE_DEVICE_TABLE(of, sbtsi_of_match); - -static struct i2c_driver sbtsi_driver = { +static struct auxiliary_driver sbtsi_driver = { .driver = { .name = "sbtsi", - .of_match_table = of_match_ptr(sbtsi_of_match), }, .probe = sbtsi_probe, .id_table = sbtsi_id, }; - -module_i2c_driver(sbtsi_driver); +module_auxiliary_driver(sbtsi_driver); MODULE_AUTHOR("Kun Yi <kunyi@google.com>"); MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor"); diff --git a/drivers/hwmon/socfpga-hwmon.c b/drivers/hwmon/socfpga-hwmon.c new file mode 100644 index 000000000000..5b43274d0aa2 --- /dev/null +++ b/drivers/hwmon/socfpga-hwmon.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Altera SoC FPGA hardware monitoring driver + * + * Copyright (c) 2026 Altera Corporation + * + * Authors: + * Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com> + * Tze Yee Ng <tze.yee.ng@altera.com> + */ + +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/firmware/intel/stratix10-svc-client.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#define HWMON_TIMEOUT msecs_to_jiffies(SVC_HWMON_REQUEST_TIMEOUT_MS) +#define HWMON_RETRY_SLEEP_US 1000U +#define HWMON_ASYNC_MSG_RETRY 3U +#define SOCFPGA_HWMON_MAXSENSORS 16 +#define SOCFPGA_HWMON_CHANNEL_MASK GENMASK(15, 0) +#define SOCFPGA_HWMON_PAGE_SHIFT 16 +#define SOCFPGA_HWMON_CHAN(page, channel) \ + (((page) << SOCFPGA_HWMON_PAGE_SHIFT) | \ + ((channel) & SOCFPGA_HWMON_CHANNEL_MASK)) +#define SOCFPGA_HWMON_ATTR_VISIBLE 0444 +/* Temperature from SDM is signed Q8.8 degrees Celsius (8 fractional bits). */ +#define SOCFPGA_HWMON_TEMP_FRAC_BITS 8 +#define SOCFPGA_HWMON_TEMP_FRAC_DIV BIT(SOCFPGA_HWMON_TEMP_FRAC_BITS) +#define SOCFPGA_HWMON_TEMP_MDEG_SCALE 1000 +/* Voltage from SDM is unsigned Q16 volts (16 fractional bits). */ +#define SOCFPGA_HWMON_VOLT_FRAC_BITS 16 +#define SOCFPGA_HWMON_VOLT_FRAC_DIV BIT(SOCFPGA_HWMON_VOLT_FRAC_BITS) +#define SOCFPGA_HWMON_VOLT_MV_SCALE 1000 + +#define ETEMP_INACTIVE 0x80000000U +#define ETEMP_TOO_OLD 0x80000001U +#define ETEMP_NOT_PRESENT 0x80000002U +#define ETEMP_TIMEOUT 0x80000003U +#define ETEMP_CORRUPT 0x80000004U +#define ETEMP_BUSY 0x80000005U +#define ETEMP_NOT_INITIALIZED 0x800000FFU + +struct socfpga_hwmon_channel { + u32 reg; + const char *label; +}; + +struct socfpga_hwmon_board_data { + const struct socfpga_hwmon_channel *temp; + unsigned int num_temp; + const struct socfpga_hwmon_channel *volt; + unsigned int num_volt; +}; + +struct socfpga_hwmon_priv { + struct stratix10_svc_chan *chan; + struct stratix10_svc_client client; + struct completion completion; + struct mutex lock; /* protect SVC calls */ + bool async; + int last_err; /* sync-mode SVC result; 0 on success */ + u32 temperature; + u32 voltage; + int temperature_channels; + int voltage_channels; + const char *temp_chan_names[SOCFPGA_HWMON_MAXSENSORS]; + const char *volt_chan_names[SOCFPGA_HWMON_MAXSENSORS]; + u32 temp_chan[SOCFPGA_HWMON_MAXSENSORS]; + u32 volt_chan[SOCFPGA_HWMON_MAXSENSORS]; +}; + +static umode_t socfpga_hwmon_is_visible(const void *dev, + enum hwmon_sensor_types type, + u32 attr, int chan) +{ + const struct socfpga_hwmon_priv *priv = dev; + + switch (type) { + case hwmon_temp: + if (chan < priv->temperature_channels) + return SOCFPGA_HWMON_ATTR_VISIBLE; + return 0; + case hwmon_in: + if (chan < priv->voltage_channels) + return SOCFPGA_HWMON_ATTR_VISIBLE; + return 0; + default: + return 0; + } +} + +static void socfpga_hwmon_readtemp_cb(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct socfpga_hwmon_priv *priv = client->priv; + + priv->last_err = -EIO; + if (data->status == BIT(SVC_STATUS_OK)) { + priv->last_err = 0; + priv->temperature = (u32)*(unsigned long *)data->kaddr1; + } else if (data->kaddr1) { + dev_err(client->dev, "%s failed with status 0x%x, value 0x%lx\n", + __func__, data->status, + *(unsigned long *)data->kaddr1); + } else { + dev_err(client->dev, "%s failed with status 0x%x\n", + __func__, data->status); + } + + complete(&priv->completion); +} + +static void socfpga_hwmon_readvolt_cb(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct socfpga_hwmon_priv *priv = client->priv; + + priv->last_err = -EIO; + if (data->status == BIT(SVC_STATUS_OK)) { + priv->last_err = 0; + priv->voltage = (u32)*(unsigned long *)data->kaddr1; + } else if (data->kaddr1) { + dev_err(client->dev, "%s failed with status 0x%x, value 0x%lx\n", + __func__, data->status, + *(unsigned long *)data->kaddr1); + } else { + dev_err(client->dev, "%s failed with status 0x%x\n", + __func__, data->status); + } + + complete(&priv->completion); +} + +static int socfpga_hwmon_parse_temp(long *val, u32 temperature) +{ + switch (temperature) { + case ETEMP_INACTIVE: + case ETEMP_NOT_PRESENT: + case ETEMP_CORRUPT: + case ETEMP_NOT_INITIALIZED: + return -EOPNOTSUPP; + case ETEMP_TIMEOUT: + case ETEMP_BUSY: + case ETEMP_TOO_OLD: + return -EAGAIN; + default: + /* SDM returns a 16-bit signed Q8.8 value in the low 16 bits. */ + *val = (long)(s16)(temperature & SOCFPGA_HWMON_CHANNEL_MASK) * + SOCFPGA_HWMON_TEMP_MDEG_SCALE / SOCFPGA_HWMON_TEMP_FRAC_DIV; + return 0; + } +} + +static int socfpga_hwmon_encode_temp_arg(u32 reg, u64 *arg) +{ + u32 page = (reg >> SOCFPGA_HWMON_PAGE_SHIFT) & SOCFPGA_HWMON_CHANNEL_MASK; + u32 channel = reg & SOCFPGA_HWMON_CHANNEL_MASK; + + if (channel >= SOCFPGA_HWMON_MAXSENSORS) + return -EINVAL; + + *arg = (1ULL << channel) | ((u64)page << SOCFPGA_HWMON_PAGE_SHIFT); + return 0; +} + +static int socfpga_hwmon_encode_volt_arg(u32 reg, u64 *arg) +{ + u32 channel = reg & SOCFPGA_HWMON_CHANNEL_MASK; + + if (channel >= SOCFPGA_HWMON_MAXSENSORS) + return -EINVAL; + + *arg = 1ULL << channel; + return 0; +} + +static int socfpga_hwmon_async_read(struct device *dev, + enum hwmon_sensor_types type, + struct stratix10_svc_client_msg *msg) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + struct stratix10_svc_cb_data data = {}; + unsigned long deadline = jiffies + HWMON_TIMEOUT; + void *handle = NULL; + int status, index, ret; + + for (index = 0; index < HWMON_ASYNC_MSG_RETRY; index++) { + status = stratix10_svc_async_send(priv->chan, msg, &handle, + NULL, NULL); + if (status == 0) + break; + dev_warn(dev, "Failed to send async message: %d\n", status); + usleep_range(HWMON_RETRY_SLEEP_US, HWMON_RETRY_SLEEP_US * 2); + } + + if (status && !handle) { + dev_err(dev, "Failed to send async message after %u retries: %d\n", + HWMON_ASYNC_MSG_RETRY, status); + return status; + } + + ret = -ETIMEDOUT; + while (!time_after(jiffies, deadline)) { + status = stratix10_svc_async_poll(priv->chan, handle, &data); + if (status == -EAGAIN) { + /* still in progress */ + } else if (status < 0) { + ret = status; + break; + } else if (status == 0) { + ret = 0; + break; + } + usleep_range(HWMON_RETRY_SLEEP_US, HWMON_RETRY_SLEEP_US * 2); + } + + if (ret) { + dev_err(dev, "Failed to get async response\n"); + goto done; + } + + if (data.status) { + dev_err(dev, "%s returned 0x%x from SDM\n", __func__, + data.status); + ret = -EFAULT; + goto done; + } + + if (type == hwmon_temp) + priv->temperature = (u32)*(unsigned long *)data.kaddr1; + else + priv->voltage = (u32)*(unsigned long *)data.kaddr1; + + ret = 0; + +done: + stratix10_svc_async_done(priv->chan, handle); + return ret; +} + +static int socfpga_hwmon_sync_read(struct device *dev, + enum hwmon_sensor_types type, + struct stratix10_svc_client_msg *msg) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + int ret; + + reinit_completion(&priv->completion); + + if (type == hwmon_temp) + priv->client.receive_cb = socfpga_hwmon_readtemp_cb; + else + priv->client.receive_cb = socfpga_hwmon_readvolt_cb; + + ret = stratix10_svc_send(priv->chan, msg); + if (ret < 0) + goto status_done; + + ret = wait_for_completion_timeout(&priv->completion, HWMON_TIMEOUT); + if (!ret) { + dev_err(priv->client.dev, "timeout waiting for SMC call\n"); + ret = -ETIMEDOUT; + goto status_done; + } + + ret = priv->last_err; + +status_done: + stratix10_svc_done(priv->chan); + return ret; +} + +static int socfpga_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int chan, long *val) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + struct stratix10_svc_client_msg msg = {0}; + int ret; + + if (chan >= SOCFPGA_HWMON_MAXSENSORS) + return -EOPNOTSUPP; + + switch (type) { + case hwmon_temp: + ret = socfpga_hwmon_encode_temp_arg(priv->temp_chan[chan], + &msg.arg[0]); + if (ret) + return ret; + msg.command = COMMAND_HWMON_READTEMP; + break; + case hwmon_in: + ret = socfpga_hwmon_encode_volt_arg(priv->volt_chan[chan], + &msg.arg[0]); + if (ret) + return ret; + msg.command = COMMAND_HWMON_READVOLT; + break; + default: + return -EOPNOTSUPP; + } + + guard(mutex)(&priv->lock); + if (priv->async) + ret = socfpga_hwmon_async_read(dev, type, &msg); + else + ret = socfpga_hwmon_sync_read(dev, type, &msg); + if (ret) + return ret; + + if (type == hwmon_temp) + ret = socfpga_hwmon_parse_temp(val, priv->temperature); + else + /* SDM returns Q16 volts; convert to hwmon millivolts. */ + *val = (long)priv->voltage * SOCFPGA_HWMON_VOLT_MV_SCALE / + SOCFPGA_HWMON_VOLT_FRAC_DIV; + return ret; +} + +static int socfpga_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int chan, const char **str) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + + switch (type) { + case hwmon_in: + *str = priv->volt_chan_names[chan]; + return 0; + case hwmon_temp: + *str = priv->temp_chan_names[chan]; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_ops socfpga_hwmon_ops = { + .is_visible = socfpga_hwmon_is_visible, + .read = socfpga_hwmon_read, + .read_string = socfpga_hwmon_read_string, +}; + +static const struct hwmon_channel_info *socfpga_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + NULL +}; + +static const struct hwmon_chip_info socfpga_hwmon_chip_info = { + .ops = &socfpga_hwmon_ops, + .info = socfpga_hwmon_info, +}; + +static const struct socfpga_hwmon_channel s10_hwmon_volt_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 2), "0.8V VCC" }, + { SOCFPGA_HWMON_CHAN(0, 3), "1.8V VCCIO_SDM" }, + { SOCFPGA_HWMON_CHAN(0, 6), "0.9V VCCERAM" }, +}; + +static const struct socfpga_hwmon_channel s10_hwmon_temp_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 0), "Main Die SDM" }, +}; + +static const struct socfpga_hwmon_board_data s10_hwmon_board = { + .temp = s10_hwmon_temp_channels, + .num_temp = ARRAY_SIZE(s10_hwmon_temp_channels), + .volt = s10_hwmon_volt_channels, + .num_volt = ARRAY_SIZE(s10_hwmon_volt_channels), +}; + +static const struct socfpga_hwmon_channel agilex_hwmon_volt_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 2), "0.8V VCC" }, + { SOCFPGA_HWMON_CHAN(0, 3), "1.8V VCCIO_SDM" }, + { SOCFPGA_HWMON_CHAN(0, 4), "1.8V VCCPT" }, + { SOCFPGA_HWMON_CHAN(0, 5), "1.2V VCCCRCORE" }, + { SOCFPGA_HWMON_CHAN(0, 6), "0.9V VCCH" }, + { SOCFPGA_HWMON_CHAN(0, 7), "0.8V VCCL" }, +}; + +static const struct socfpga_hwmon_channel agilex_hwmon_temp_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 0), "Main Die SDM" }, + { SOCFPGA_HWMON_CHAN(1, 0), "Main Die corner bottom left max" }, + { SOCFPGA_HWMON_CHAN(2, 0), "Main Die corner top left max" }, + { SOCFPGA_HWMON_CHAN(3, 0), "Main Die corner bottom right max" }, + { SOCFPGA_HWMON_CHAN(4, 0), "Main Die corner top right max" }, +}; + +static const struct socfpga_hwmon_board_data agilex_hwmon_board = { + .temp = agilex_hwmon_temp_channels, + .num_temp = ARRAY_SIZE(agilex_hwmon_temp_channels), + .volt = agilex_hwmon_volt_channels, + .num_volt = ARRAY_SIZE(agilex_hwmon_volt_channels), +}; + +static const struct socfpga_hwmon_board_data * +socfpga_hwmon_get_board(struct device *dev) +{ + struct device_node *np = dev->of_node; + + if (!np) + return NULL; + + if (of_device_is_compatible(np, "intel,stratix10-svc")) + return &s10_hwmon_board; + if (of_device_is_compatible(np, "intel,agilex-svc")) + return &agilex_hwmon_board; + + return NULL; +} + +static int socfpga_hwmon_init_channels(struct device *dev, + const struct socfpga_hwmon_board_data *board, + struct socfpga_hwmon_priv *priv) +{ + unsigned int i; + + if (board->num_temp > SOCFPGA_HWMON_MAXSENSORS || + board->num_volt > SOCFPGA_HWMON_MAXSENSORS) + return -EINVAL; + + for (i = 0; i < board->num_temp; i++) { + priv->temp_chan_names[i] = board->temp[i].label; + priv->temp_chan[i] = board->temp[i].reg; + } + priv->temperature_channels = board->num_temp; + + for (i = 0; i < board->num_volt; i++) { + priv->volt_chan_names[i] = board->volt[i].label; + priv->volt_chan[i] = board->volt[i].reg; + } + priv->voltage_channels = board->num_volt; + + return 0; +} + +static void socfpga_hwmon_release_svc(void *data) +{ + struct socfpga_hwmon_priv *priv = data; + + if (priv->async) + stratix10_svc_remove_async_client(priv->chan); + stratix10_svc_free_channel(priv->chan); +} + +static int socfpga_hwmon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + const struct socfpga_hwmon_board_data *board; + struct socfpga_hwmon_priv *priv; + struct device *hwmon_dev; + int ret; + + if (!parent || !parent->of_node) { + dev_err(dev, "missing parent device node\n"); + return -ENODEV; + } + + board = socfpga_hwmon_get_board(parent); + if (!board) { + dev_err(dev, "unsupported service layer compatible\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client.dev = dev; + priv->client.priv = priv; + init_completion(&priv->completion); + mutex_init(&priv->lock); + + ret = socfpga_hwmon_init_channels(dev, board, priv); + if (ret) + return ret; + + priv->chan = stratix10_svc_request_channel_byname(&priv->client, + SVC_CLIENT_HWMON); + if (IS_ERR(priv->chan)) { + ret = PTR_ERR(priv->chan); + if (ret == -EPROBE_DEFER) + dev_dbg(dev, "service channel %s not ready, deferring probe\n", + SVC_CLIENT_HWMON); + else + dev_err(dev, "couldn't get service channel %s: %d\n", + SVC_CLIENT_HWMON, ret); + return ret; + } + + ret = stratix10_svc_add_async_client(priv->chan, false); + switch (ret) { + case 0: + priv->async = true; + break; + case -EINVAL: + case -EOPNOTSUPP: + /* + * stratix10_svc_add_async_client() returns -EINVAL when the + * async controller is not initialized; fall back to sync mode. + */ + dev_dbg(dev, "async operations not supported, using sync mode\n"); + priv->async = false; + break; + default: + dev_err(dev, "failed to add async client: %d\n", ret); + stratix10_svc_free_channel(priv->chan); + return ret; + } + + ret = devm_add_action_or_reset(dev, socfpga_hwmon_release_svc, priv); + if (ret) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "socfpga_hwmon", + priv, + &socfpga_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + platform_set_drvdata(pdev, priv); + return 0; +} + +static struct platform_driver socfpga_hwmon_driver = { + .probe = socfpga_hwmon_probe, + .driver = { + .name = "socfpga-hwmon", + }, +}; +module_platform_driver(socfpga_hwmon_driver); + +MODULE_AUTHOR("Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>"); +MODULE_AUTHOR("Tze Yee Ng <tze.yee.ng@altera.com>"); +MODULE_DESCRIPTION("Altera SoC FPGA hardware monitoring driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:socfpga-hwmon"); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 762109307b86..b3c67e96a82a 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -717,26 +717,19 @@ static DEVICE_ATTR_RW(cntr_rld_event); static ssize_t cntr_val_show(struct device *dev, struct device_attribute *attr, char *buf) { - int i, ret = 0; u32 val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; if (!coresight_get_mode(drvdata->csdev)) { spin_lock(&drvdata->spinlock); - for (i = 0; i < drvdata->nr_cntr; i++) - ret += sprintf(buf, "counter %d: %x\n", - i, config->cntr_val[i]); + val = config->cntr_val[config->cntr_idx]; spin_unlock(&drvdata->spinlock); - return ret; - } - - for (i = 0; i < drvdata->nr_cntr; i++) { - val = etm_readl(drvdata, ETMCNTVRn(i)); - ret += sprintf(buf, "counter %d: %x\n", i, val); + } else { + val = etm_readl(drvdata, ETMCNTVRn(config->cntr_idx)); } - return ret; + return sysfs_emit(buf, "%#x\n", val); } static ssize_t cntr_val_store(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c index c302072b293a..e1a59b434505 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -76,7 +76,7 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata, } else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) { /* sequencer state control registers */ idx = (offset & GENMASK(3, 0)) / 4; - if (idx < ETM_MAX_SEQ_STATES) { + if (idx < ETM_MAX_SEQ_TRANSITIONS) { reg_csdev->driver_regval = &drvcfg->seq_ctrl[idx]; err = 0; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 14bb31bd6a0b..2247ad55d444 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -93,7 +93,7 @@ static int etm4_probe_cpu(unsigned int cpu); static bool etm4x_sspcicrn_present(struct etmv4_drvdata *drvdata, int n) { return (n < drvdata->nr_ss_cmp) && - drvdata->nr_pe && + drvdata->nr_pe_cmp && (drvdata->config.ss_status[n] & TRCSSCSRn_PC); } @@ -542,7 +542,8 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) etm4x_relaxed_write32(csa, config->vissctlr, TRCVISSCTLR); if (drvdata->nr_pe_cmp) etm4x_relaxed_write32(csa, config->vipcssctlr, TRCVIPCSSCTLR); - for (i = 0; i < drvdata->nrseqstate - 1; i++) + + for (i = 0; i < drvdata->nr_seq_ctrls; i++) etm4x_relaxed_write32(csa, config->seq_ctrl[i], TRCSEQEVRn(i)); if (drvdata->nrseqstate) { etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR); @@ -756,8 +757,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev, .ATTR_CFG_FLD_timestamp_CFG = U64_MAX, }; struct perf_event_attr *attr = &event->attr; - unsigned long cfg_hash; - int preset, cc_threshold; + int cc_threshold; u8 ts_level; /* Clear configuration from previous run */ @@ -843,16 +843,6 @@ static int etm4_parse_event_config(struct coresight_device *csdev, /* bit[12], Return stack enable bit */ config->cfg |= TRCCONFIGR_RS; - /* - * Set any selected configuration and preset. A zero configid means no - * configuration active, preset = 0 means no preset selected. - */ - cfg_hash = ATTR_CFG_GET_FLD(attr, configid); - if (cfg_hash) { - preset = ATTR_CFG_GET_FLD(attr, preset); - ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); - } - /* branch broadcast - enable if selected and supported */ if (ATTR_CFG_GET_FLD(attr, branch_broadcast)) { if (!drvdata->trcbb) { @@ -876,7 +866,9 @@ static int etm4_enable_perf(struct coresight_device *csdev, struct coresight_path *path) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - int ret; + struct perf_event_attr *attr = &event->attr; + unsigned long cfg_hash; + int ret, preset; if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) return -EINVAL; @@ -887,7 +879,19 @@ static int etm4_enable_perf(struct coresight_device *csdev, /* Configure the tracer based on the session's specifics */ ret = etm4_parse_event_config(csdev, event); if (ret) - goto out; + goto err; + + /* + * Set any selected configuration and preset. A zero configid means no + * configuration active, preset = 0 means no preset selected. + */ + cfg_hash = ATTR_CFG_GET_FLD(attr, configid); + if (cfg_hash) { + preset = ATTR_CFG_GET_FLD(attr, preset); + ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); + if (ret) + goto err; + } drvdata->trcid = path->trace_id; @@ -896,16 +900,19 @@ static int etm4_enable_perf(struct coresight_device *csdev, /* And enable it */ ret = etm4_enable_hw(drvdata); - -out: - /* Failed to start tracer; roll back to DISABLED mode */ if (ret) { - coresight_set_mode(csdev, CS_MODE_DISABLED); - return ret; + if (cfg_hash) + cscfg_csdev_disable_active_config(csdev); + goto err; } csdev->path = path; return 0; + +err: + /* Failed to start tracer; roll back to DISABLED mode */ + coresight_set_mode(csdev, CS_MODE_DISABLED); + return ret; } static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path) @@ -919,8 +926,10 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa cscfg_config_sysfs_get_active_cfg(&cfg_hash, &preset); if (cfg_hash) { ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); - if (ret) + if (ret) { + etm4_release_trace_id(drvdata); return ret; + } } raw_spin_lock(&drvdata->spinlock); @@ -1508,6 +1517,8 @@ static void etm4_init_arch_data(void *info) drvdata->lpoverride = (etmidr5 & TRCIDR5_LPOVERRIDE) && (!drvdata->skip_power_up); /* NUMSEQSTATE, bits[27:25] number of sequencer states implemented */ drvdata->nrseqstate = FIELD_GET(TRCIDR5_NUMSEQSTATE_MASK, etmidr5); + if (drvdata->nrseqstate) + drvdata->nr_seq_ctrls = ETM_MAX_SEQ_TRANSITIONS; /* NUMCNTR, bits[30:28] number of counters available for tracing */ drvdata->nr_cntr = FIELD_GET(TRCIDR5_NUMCNTR_MASK, etmidr5); @@ -1896,7 +1907,7 @@ static int etm4_cpu_save(struct coresight_device *csdev) if (drvdata->nr_pe_cmp) state->trcvipcssctlr = etm4x_read32(csa, TRCVIPCSSCTLR); - for (i = 0; i < drvdata->nrseqstate - 1; i++) + for (i = 0; i < drvdata->nr_seq_ctrls; i++) state->trcseqevr[i] = etm4x_read32(csa, TRCSEQEVRn(i)); if (drvdata->nrseqstate) { @@ -2009,7 +2020,7 @@ static void etm4_cpu_restore(struct coresight_device *csdev) if (drvdata->nr_pe_cmp) etm4x_relaxed_write32(csa, state->trcvipcssctlr, TRCVIPCSSCTLR); - for (i = 0; i < drvdata->nrseqstate - 1; i++) + for (i = 0; i < drvdata->nr_seq_ctrls; i++) etm4x_relaxed_write32(csa, state->trcseqevr[i], TRCSEQEVRn(i)); if (drvdata->nrseqstate) { diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index e9eeea6240d5..cc6cdd3ae29d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -223,7 +223,7 @@ static ssize_t reset_store(struct device *dev, config->vipcssctlr = 0x0; /* Disable seq events */ - for (i = 0; i < drvdata->nrseqstate-1; i++) + for (i = 0; i < drvdata->nr_seq_ctrls; i++) config->seq_ctrl[i] = 0x0; config->seq_rst = 0x0; config->seq_state = 0x0; @@ -1395,9 +1395,11 @@ static ssize_t seq_idx_store(struct device *dev, struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etmv4_config *config = &drvdata->config; + if (!drvdata->nr_seq_ctrls) + return -ENOTSUPP; if (kstrtoul(buf, 16, &val)) return -EINVAL; - if (val >= drvdata->nrseqstate - 1) + if (val >= drvdata->nr_seq_ctrls) return -EINVAL; /* diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 89d81ce4e04e..df9e0748d71e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -614,6 +614,7 @@ static inline u32 etm4_res_sel_pair(u8 res_sel_idx) #define ETM_MAX_NR_PE 8 #define ETMv4_MAX_CNTR 4 #define ETM_MAX_SEQ_STATES 4 +#define ETM_MAX_SEQ_TRANSITIONS 3 #define ETM_MAX_EXT_INP_SEL 4 #define ETM_MAX_EXT_INP 256 #define ETM_MAX_EXT_OUT 4 @@ -877,7 +878,7 @@ struct etmv4_config { u32 vipcssctlr; u8 seq_idx; u8 syncfreq; - u32 seq_ctrl[ETM_MAX_SEQ_STATES]; + u32 seq_ctrl[ETM_MAX_SEQ_TRANSITIONS]; u32 seq_rst; u32 seq_state; u8 cntr_idx; @@ -928,7 +929,7 @@ struct etmv4_save_state { u32 trcvissctlr; u32 trcvipcssctlr; - u32 trcseqevr[ETM_MAX_SEQ_STATES]; + u32 trcseqevr[ETM_MAX_SEQ_TRANSITIONS]; u32 trcseqrstevr; u32 trcseqstr; u32 trcextinselr; @@ -953,11 +954,6 @@ struct etmv4_save_state { u32 trcclaimset; - u32 cntr_val[ETMv4_MAX_CNTR]; - u32 seq_state; - u32 vinst_ctrl; - u32 ss_status[ETM_MAX_SS_CMP]; - u32 trcpdcr; }; @@ -981,6 +977,7 @@ struct etmv4_save_state { * @numcidc: Number of contextID comparators. * @numvmidc: Number of VMID comparators. * @nrseqstate: The number of sequencer states that are implemented. + * @nr_seq_ctrls: The number of sequence state transition control registers. * @nr_event: Indicates how many events the trace unit support. * @nr_resource:The number of resource selection pairs available for tracing. * @nr_ss_cmp: Number of single-shot comparator controls that are available. @@ -1046,6 +1043,7 @@ struct etmv4_drvdata { u8 numextinsel; u8 numvmidc; u8 nrseqstate; + u8 nr_seq_ctrls; u8 nr_event; u8 nr_resource; u8 nr_ss_cmp; diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 233c4c32513c..52f168f8b459 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -171,13 +171,13 @@ static bool hisi_ptt_wait_trace_hw_idle(struct hisi_ptt *hisi_ptt) HISI_PTT_WAIT_TRACE_TIMEOUT_US); } -static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) +static bool hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) { u32 val; - readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, - val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, - HISI_PTT_RESET_TIMEOUT_US); + return !readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, + val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, + HISI_PTT_RESET_TIMEOUT_US); } static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt) @@ -194,7 +194,6 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) { struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; u32 val; - int i; /* Check device idle before start trace */ if (!hisi_ptt_wait_trace_hw_idle(hisi_ptt)) { @@ -202,14 +201,18 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) return -EBUSY; } - ctrl->started = true; - /* Reset the DMA before start tracing */ val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); val |= HISI_PTT_TRACE_CTRL_RST; writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); - hisi_ptt_wait_dma_reset_done(hisi_ptt); + if (!hisi_ptt_wait_dma_reset_done(hisi_ptt)) { + pci_err(hisi_ptt->pdev, "timed out waiting for DMA reset\n"); + val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + val &= ~HISI_PTT_TRACE_CTRL_RST; + writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + return -ETIMEDOUT; + } val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); val &= ~HISI_PTT_TRACE_CTRL_RST; @@ -218,10 +221,6 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) /* Reset the index of current buffer */ hisi_ptt->trace_ctrl.buf_index = 0; - /* Zero the trace buffers */ - for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; i++) - memset(ctrl->trace_buf[i].addr, 0, HISI_PTT_TRACE_BUF_SIZE); - /* Clear the interrupt status */ writel(HISI_PTT_TRACE_INT_STAT_MASK, hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT); writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_INT_MASK); @@ -234,6 +233,8 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) if (!hisi_ptt->trace_ctrl.is_port) val |= HISI_PTT_TRACE_CTRL_FILTER_MODE; + ctrl->started = true; + /* Start the Trace */ val |= HISI_PTT_TRACE_CTRL_EN; writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 661127aed2f9..652557a5b851 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -92,6 +92,7 @@ source "drivers/iio/common/Kconfig" source "drivers/iio/dac/Kconfig" source "drivers/iio/dummy/Kconfig" source "drivers/iio/filter/Kconfig" +source "drivers/iio/flow/Kconfig" source "drivers/iio/frequency/Kconfig" source "drivers/iio/gyro/Kconfig" source "drivers/iio/health/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index cb80ef837e84..f03a4100c800 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -29,6 +29,7 @@ obj-y += dac/ obj-y += dummy/ obj-y += gyro/ obj-y += filter/ +obj-y += flow/ obj-y += frequency/ obj-y += health/ obj-y += humidity/ diff --git a/drivers/iio/TODO b/drivers/iio/TODO index 2ace27d1ac62..7b05308248d7 100644 --- a/drivers/iio/TODO +++ b/drivers/iio/TODO @@ -5,12 +5,17 @@ Documentation tree - Yaml conversions for abandoned drivers - ABI Documentation - - Audit driviers/iio/staging/Documentation + - Audit drivers/staging/iio/Documentation -- Converting drivers from device tree centric to more generic -property handlers. +- Converting drivers from device tree centric to generic property handlers. - Refactor old platform_data constructs from drivers and convert it -to state struct and using property handlers and readers. + to state struct and using property handlers and readers. + +- Convert selected IIO drivers to use fully device managed resource APIs. + +- Evaluate and possibly adopt cleanup.h helpers (guard()(), __free(), et cetera) + in drivers where manual resource management is still used and there is a + significant improvement to the code flow and complexity to be had. Mailing list: linux-iio@vger.kernel.org diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index 0315f4bfd69a..4fa1b04f75b6 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -60,8 +60,8 @@ static const struct adxl345_chip_info adxl375_spi_info = { }; static const struct spi_device_id adxl345_spi_id[] = { - { "adxl345", (kernel_ulong_t)&adxl345_spi_info }, - { "adxl375", (kernel_ulong_t)&adxl375_spi_info }, + { .name = "adxl345", .driver_data = (kernel_ulong_t)&adxl345_spi_info }, + { .name = "adxl375", .driver_data = (kernel_ulong_t)&adxl375_spi_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl345_spi_id); diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index 68cb2557f390..080c9f6f9a11 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -768,8 +768,7 @@ static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq) ret = devm_request_irq(data->dev, irq, &iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, "adxl355_irq", data->dready_trig); if (ret) - return dev_err_probe(data->dev, ret, "request irq %d failed\n", - irq); + return ret; ret = devm_iio_trigger_register(data->dev, data->dready_trig); if (ret) diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c index 437be2f1d53c..d2209ca8ca6e 100644 --- a/drivers/iio/accel/adxl355_spi.c +++ b/drivers/iio/accel/adxl355_spi.c @@ -40,8 +40,8 @@ static int adxl355_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl355_spi_id[] = { - { "adxl355", (kernel_ulong_t)&adxl35x_chip_info[ADXL355] }, - { "adxl359", (kernel_ulong_t)&adxl35x_chip_info[ADXL359] }, + { .name = "adxl355", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL355] }, + { .name = "adxl359", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL359] }, { } }; MODULE_DEVICE_TABLE(spi, adxl355_spi_id); diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index 8c3de11a10a3..4ff1c7a0988b 100644 --- a/drivers/iio/accel/adxl367.c +++ b/drivers/iio/accel/adxl367.c @@ -1486,7 +1486,7 @@ int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, adxl367_irq_handler, IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + return ret; return devm_iio_device_register(dev, indio_dev); } diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c index 785652c7fc92..1d1418452bdc 100644 --- a/drivers/iio/accel/adxl367_spi.c +++ b/drivers/iio/accel/adxl367_spi.c @@ -137,7 +137,7 @@ static int adxl367_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl367_spi_id[] = { - { "adxl367", 0 }, + { .name = "adxl367" }, { } }; MODULE_DEVICE_TABLE(spi, adxl367_spi_id); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 25fdb4254372..128e148c4e96 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -34,8 +34,8 @@ static int adxl372_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl372_spi_id[] = { - { "adxl371", (kernel_ulong_t)&adxl371_chip_info }, - { "adxl372", (kernel_ulong_t)&adxl372_chip_info }, + { .name = "adxl371", .driver_data = (kernel_ulong_t)&adxl371_chip_info }, + { .name = "adxl372", .driver_data = (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl372_spi_id); diff --git a/drivers/iio/accel/adxl380_spi.c b/drivers/iio/accel/adxl380_spi.c index ee3e6338b53d..ae8467f4e6c0 100644 --- a/drivers/iio/accel/adxl380_spi.c +++ b/drivers/iio/accel/adxl380_spi.c @@ -34,10 +34,10 @@ static int adxl380_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl380_spi_id[] = { - { "adxl318", (kernel_ulong_t)&adxl318_chip_info }, - { "adxl319", (kernel_ulong_t)&adxl319_chip_info }, - { "adxl380", (kernel_ulong_t)&adxl380_chip_info }, - { "adxl382", (kernel_ulong_t)&adxl382_chip_info }, + { .name = "adxl318", .driver_data = (kernel_ulong_t)&adxl318_chip_info }, + { .name = "adxl319", .driver_data = (kernel_ulong_t)&adxl319_chip_info }, + { .name = "adxl380", .driver_data = (kernel_ulong_t)&adxl380_chip_info }, + { .name = "adxl382", .driver_data = (kernel_ulong_t)&adxl382_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl380_spi_id); diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 62bda8d76691..e7dbbd4766dd 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -988,10 +988,8 @@ static int bma180_probe(struct i2c_client *client) iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, "bma180_event", data->trig); - if (ret) { - dev_err(dev, "unable to request IRQ\n"); + if (ret) goto err_trigger_free; - } data->trig->ops = &bma180_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); diff --git a/drivers/iio/accel/bma220_core.c b/drivers/iio/accel/bma220_core.c index 269e2b720ddb..b83abfdd84fd 100644 --- a/drivers/iio/accel/bma220_core.c +++ b/drivers/iio/accel/bma220_core.c @@ -544,8 +544,7 @@ int bma220_common_probe(struct device *dev, struct regmap *regmap, int irq) &bma220_irq_handler, IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(dev, ret, - "request irq %d failed\n", irq); + return ret; } ret = devm_add_action_or_reset(dev, bma220_deinit, data); diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index d897aec1c9f2..d6c1087cc3c0 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -25,7 +25,7 @@ static int bma220_spi_probe(struct spi_device *spi) } static const struct spi_device_id bma220_spi_id[] = { - { "bma220", 0 }, + { .name = "bma220" }, { } }; diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 05f72707f830..7e3cff4cca91 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -1789,8 +1789,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, int irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(data->dev, ret, - "request irq %d failed\n", irq); + return ret; } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c index 4b89e313704f..7ac0a7881b59 100644 --- a/drivers/iio/accel/bma400_spi.c +++ b/drivers/iio/accel/bma400_spi.c @@ -87,7 +87,7 @@ static int bma400_spi_probe(struct spi_device *spi) } static const struct spi_device_id bma400_spi_ids[] = { - { "bma400", 0 }, + { .name = "bma400" }, { } }; MODULE_DEVICE_TABLE(spi, bma400_spi_ids); diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index dc8a6285cf3d..46bb36d9366e 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -988,8 +988,10 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, do_div(sample_period, count); tstamp = data->timestamp - (count - 1) * sample_period; - if (samples && count > samples) - count = samples; + if (samples) + count = min3(count, samples, BMC150_ACCEL_FIFO_LENGTH); + else + count = min(count, BMC150_ACCEL_FIFO_LENGTH); count = min_t(u8, count, BMC150_ACCEL_FIFO_LENGTH); diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c index 7d2be6f63538..405bd7b2d199 100644 --- a/drivers/iio/accel/bmc150-accel-spi.c +++ b/drivers/iio/accel/bmc150-accel-spi.c @@ -52,15 +52,15 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); static const struct spi_device_id bmc150_accel_id[] = { - {"bma222"}, - {"bma222e"}, - {"bma250e"}, - {"bma253"}, - {"bma255"}, - {"bma280"}, - {"bmc150_accel"}, - {"bmc156_accel", BOSCH_BMC156}, - {"bmi055_accel"}, + { .name = "bma222", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma222e", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma250e", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma253", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma255", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma280", .driver_data = BOSCH_UNKNOWN }, + { .name = "bmc150_accel", .driver_data = BOSCH_UNKNOWN }, + { .name = "bmc156_accel", .driver_data = BOSCH_BMC156 }, + { .name = "bmi055_accel", .driver_data = BOSCH_UNKNOWN }, { } }; MODULE_DEVICE_TABLE(spi, bmc150_accel_id); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index 44cb50c76cb1..ae89e162e097 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -72,9 +72,9 @@ static const struct of_device_id bmi088_of_match[] = { MODULE_DEVICE_TABLE(of, bmi088_of_match); static const struct spi_device_id bmi088_accel_id[] = { - {"bmi085-accel", BOSCH_BMI085}, - {"bmi088-accel", BOSCH_BMI088}, - {"bmi090l-accel", BOSCH_BMI090L}, + { .name = "bmi085-accel", .driver_data = BOSCH_BMI085 }, + { .name = "bmi088-accel", .driver_data = BOSCH_BMI088 }, + { .name = "bmi090l-accel", .driver_data = BOSCH_BMI090L }, { } }; MODULE_DEVICE_TABLE(spi, bmi088_accel_id); diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c index fe35a1270786..6f0497ab6133 100644 --- a/drivers/iio/accel/dmard09.c +++ b/drivers/iio/accel/dmard09.c @@ -8,6 +8,7 @@ #include <linux/unaligned.h> #include <linux/module.h> #include <linux/i2c.h> +#include <linux/units.h> #include <linux/iio/iio.h> #define DMARD09_DRV_NAME "dmard09" @@ -79,6 +80,12 @@ static int dmard09_read_raw(struct iio_dev *indio_dev, *val = accel; return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + /* 1 g / 32 LSB, in m/s^2 */ + *val2 = IIO_G_TO_M_S_2(NANO / 32); + + return IIO_VAL_INT_PLUS_NANO; default: return -EINVAL; } diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index d0c2a8daef0d..18d7b09bddd2 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -969,6 +969,8 @@ static int fxls8962af_fifo_flush(struct iio_dev *indio_dev) if (!count) return 0; + count = min(count, FXLS8962AF_FIFO_LENGTH); + data->old_timestamp = data->timestamp; data->timestamp = iio_get_time_ns(indio_dev); diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c index 8936032526a0..baf3c4352f14 100644 --- a/drivers/iio/accel/fxls8962af-spi.c +++ b/drivers/iio/accel/fxls8962af-spi.c @@ -34,8 +34,8 @@ static const struct of_device_id fxls8962af_spi_of_match[] = { MODULE_DEVICE_TABLE(of, fxls8962af_spi_of_match); static const struct spi_device_id fxls8962af_spi_id_table[] = { - { "fxls8962af", fxls8962af }, - { "fxls8964af", fxls8964af }, + { .name = "fxls8962af", .driver_data = fxls8962af }, + { .name = "fxls8964af", .driver_data = fxls8964af }, { } }; MODULE_DEVICE_TABLE(spi, fxls8962af_spi_id_table); diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 737572bb44d9..eb521125f4eb 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -121,9 +121,8 @@ static const struct iio_chan_spec gravity_channels[] = { /* Channel read_raw handler */ static int accel_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct accel_3d_state *accel_state = iio_priv(indio_dev); int report_id = -1; @@ -150,7 +149,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, else { *val = 0; hid_sensor_power_state(&accel_state->common_attributes, - false); + false); return -EINVAL; } hid_sensor_power_state(&accel_state->common_attributes, false); @@ -183,10 +182,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int accel_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct accel_3d_state *accel_state = iio_priv(indio_dev); int ret = 0; @@ -222,8 +219,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data, /* Callback handler to send event after all samples are received and captured */ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); @@ -246,9 +242,9 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); @@ -280,10 +276,10 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int accel_3d_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - unsigned usage_id, - struct accel_3d_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct accel_3d_state *st) { int ret; @@ -302,10 +298,10 @@ static int accel_3d_parse_report(struct platform_device *pdev, }; } dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n", - st->accel[0].index, - st->accel[0].report_id, - st->accel[1].index, st->accel[1].report_id, - st->accel[2].index, st->accel[2].report_id); + st->accel[0].index, + st->accel[0].report_id, + st->accel[1].index, st->accel[1].report_id, + st->accel[2].index, st->accel[2].report_id); st->scale_precision = hid_sensor_format_scale( hsdev->usage, @@ -328,7 +324,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct accel_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -365,8 +361,8 @@ static int hid_accel_3d_probe(struct platform_device *pdev) return -ENOMEM; } ret = accel_3d_parse_report(pdev, hsdev, - (struct iio_chan_spec *)indio_dev->channels, - hsdev->usage, accel_state); + (struct iio_chan_spec *)indio_dev->channels, + hsdev->usage, accel_state); if (ret) { dev_err(&pdev->dev, "failed to setup attributes\n"); return ret; @@ -379,32 +375,32 @@ static int hid_accel_3d_probe(struct platform_device *pdev) atomic_set(&accel_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &accel_state->common_attributes); + &accel_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - accel_state->callbacks.send_event = accel_3d_proc_event; accel_state->callbacks.capture_sample = accel_3d_capture_sample; accel_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, - &accel_state->callbacks); + &accel_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); return ret; @@ -417,8 +413,8 @@ static void hid_accel_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct accel_3d_state *accel_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); } diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 8a13f78aeab0..f671fab3dc31 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -1435,7 +1435,7 @@ int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chi &kx022a_irq_thread_handler, IRQF_ONESHOT, name, idev); if (ret) - return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); + return ret; ret = devm_iio_trigger_register(dev, indio_trig); if (ret) diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index 31a26baba9f7..71da958d4051 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -36,7 +36,7 @@ static void kxsd9_spi_remove(struct spi_device *spi) } static const struct spi_device_id kxsd9_spi_id[] = { - {"kxsd9", 0}, + { .name = "kxsd9" }, { } }; MODULE_DEVICE_TABLE(spi, kxsd9_spi_id); diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c index aca02e83f789..872c47acf882 100644 --- a/drivers/iio/accel/mma7455_spi.c +++ b/drivers/iio/accel/mma7455_spi.c @@ -28,8 +28,8 @@ static void mma7455_spi_remove(struct spi_device *spi) } static const struct spi_device_id mma7455_spi_ids[] = { - { "mma7455", 0 }, - { "mma7456", 0 }, + { .name = "mma7455" }, + { .name = "mma7456" }, { } }; MODULE_DEVICE_TABLE(spi, mma7455_spi_ids); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 7d9cbfa01360..7e01427fd9c4 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -420,10 +420,8 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev) NULL, mma9551_event_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "mma9551_event", indio_dev); - if (ret < 0) { - dev_err(dev, "request irq %d failed\n", data->irqs[i]); + if (ret) return ret; - } dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), data->irqs[i]); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index ab43b1e0ff04..8e7aaac89d47 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1099,11 +1099,8 @@ static int mma9553_probe(struct i2c_client *client) mma9553_event_handler, IRQF_TRIGGER_RISING, "mma9553_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto out_poweroff; - } } ret = pm_runtime_set_active(&client->dev); diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index e0e73b87cba8..caf9b1b6397f 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -1059,7 +1059,7 @@ static int msa311_setup_interrupts(struct msa311_priv *msa311) msa311_irq_thread, IRQF_ONESHOT, msa311->chip_name, indio_dev); if (err) - return dev_err_probe(dev, err, "failed to request IRQ\n"); + return err; trig = devm_iio_trigger_alloc(dev, "%s-new-data", msa311->chip_name); if (!trig) diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 2034fe92bae3..a66fee828ec1 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -489,11 +489,8 @@ static int mxc4005_probe(struct i2c_client *client) iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_FALLING | IRQF_NO_THREAD, "mxc4005_event", data->dready_trig); - if (ret) { - dev_err(&client->dev, - "failed to init threaded irq\n"); + if (ret) return ret; - } data->dready_trig->ops = &mxc4005_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 573831199bba..a92a563f6a4b 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1504,10 +1504,10 @@ static int sca3000_probe(struct spi_device *spi) } static const struct spi_device_id sca3000_id[] = { - { "sca3000_d01", (kernel_ulong_t)&sca3000_chip_info_d01 }, - { "sca3000_e02", (kernel_ulong_t)&sca3000_chip_info_e02 }, - { "sca3000_e04", (kernel_ulong_t)&sca3000_chip_info_e04 }, - { "sca3000_e05", (kernel_ulong_t)&sca3000_chip_info_e05 }, + { .name = "sca3000_d01", .driver_data = (kernel_ulong_t)&sca3000_chip_info_d01 }, + { .name = "sca3000_e02", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e02 }, + { .name = "sca3000_e04", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e04 }, + { .name = "sca3000_e05", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e05 }, { } }; MODULE_DEVICE_TABLE(spi, sca3000_id); diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c index 8380b237831c..67582cc34149 100644 --- a/drivers/iio/accel/sca3300.c +++ b/drivers/iio/accel/sca3300.c @@ -664,8 +664,8 @@ static const struct of_device_id sca3300_dt_ids[] = { MODULE_DEVICE_TABLE(of, sca3300_dt_ids); static const struct spi_device_id sca3300_ids[] = { - { "sca3300" }, - { "scl3300" }, + { .name = "sca3300" }, + { .name = "scl3300" }, { } }; MODULE_DEVICE_TABLE(spi, sca3300_ids); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index e6bf8d0d4ff5..171f241c0a30 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -146,26 +146,26 @@ static int st_accel_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_accel_id_table[] = { - { LIS3DH_ACCEL_DEV_NAME }, - { LSM330D_ACCEL_DEV_NAME }, - { LSM330DL_ACCEL_DEV_NAME }, - { LSM330DLC_ACCEL_DEV_NAME }, - { LIS331DLH_ACCEL_DEV_NAME }, - { LSM330_ACCEL_DEV_NAME }, - { LSM303AGR_ACCEL_DEV_NAME }, - { LIS2DH12_ACCEL_DEV_NAME }, - { LIS2DS12_ACCEL_DEV_NAME }, - { LIS3L02DQ_ACCEL_DEV_NAME }, - { LNG2DM_ACCEL_DEV_NAME }, - { H3LIS331DL_ACCEL_DEV_NAME }, - { LIS331DL_ACCEL_DEV_NAME }, - { LIS3LV02DL_ACCEL_DEV_NAME }, - { LIS2DW12_ACCEL_DEV_NAME }, - { LIS3DHH_ACCEL_DEV_NAME }, - { LIS3DE_ACCEL_DEV_NAME }, - { LIS302DL_ACCEL_DEV_NAME }, - { LSM303C_ACCEL_DEV_NAME }, - { IIS328DQ_ACCEL_DEV_NAME }, + { .name = LIS3DH_ACCEL_DEV_NAME }, + { .name = LSM330D_ACCEL_DEV_NAME }, + { .name = LSM330DL_ACCEL_DEV_NAME }, + { .name = LSM330DLC_ACCEL_DEV_NAME }, + { .name = LIS331DLH_ACCEL_DEV_NAME }, + { .name = LSM330_ACCEL_DEV_NAME }, + { .name = LSM303AGR_ACCEL_DEV_NAME }, + { .name = LIS2DH12_ACCEL_DEV_NAME }, + { .name = LIS2DS12_ACCEL_DEV_NAME }, + { .name = LIS3L02DQ_ACCEL_DEV_NAME }, + { .name = LNG2DM_ACCEL_DEV_NAME }, + { .name = H3LIS331DL_ACCEL_DEV_NAME }, + { .name = LIS331DL_ACCEL_DEV_NAME }, + { .name = LIS3LV02DL_ACCEL_DEV_NAME }, + { .name = LIS2DW12_ACCEL_DEV_NAME }, + { .name = LIS3DHH_ACCEL_DEV_NAME }, + { .name = LIS3DE_ACCEL_DEV_NAME }, + { .name = LIS302DL_ACCEL_DEV_NAME }, + { .name = LSM303C_ACCEL_DEV_NAME }, + { .name = IIS328DQ_ACCEL_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 8b5a3e0489e9..50840c3440c4 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -7,12 +7,20 @@ * IIO driver for STK8312; 7-bit I2C address: 0x3D. */ +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> +#include <linux/errno.h> #include <linux/i2c.h> #include <linux/interrupt.h> -#include <linux/kernel.h> #include <linux/module.h> -#include <linux/delay.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/sysfs.h> #include <linux/types.h> + #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -542,11 +550,8 @@ static int stk8312_probe(struct i2c_client *client) IRQF_ONESHOT, "stk8312_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto err_power_off; - } data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index d0c53b8ac850..6c1e286c0a1d 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -7,11 +7,18 @@ * STK8BA50 7-bit I2C address: 0x18. */ +#include <linux/array_size.h> +#include <linux/bitops.h> +#include <linux/dev_printk.h> +#include <linux/errno.h> #include <linux/i2c.h> #include <linux/interrupt.h> -#include <linux/kernel.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/sysfs.h> #include <linux/types.h> + #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -431,11 +438,8 @@ static int stk8ba50_probe(struct i2c_client *client) stk8ba50_data_rdy_trig_poll, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, "stk8ba50_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto err_power_off; - } data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", diff --git a/drivers/iio/adc/88pm886-gpadc.c b/drivers/iio/adc/88pm886-gpadc.c index ff9bc5f06c18..938ba43c2c35 100644 --- a/drivers/iio/adc/88pm886-gpadc.c +++ b/drivers/iio/adc/88pm886-gpadc.c @@ -279,7 +279,7 @@ static int pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const static int pm886_gpadc_hw_enable(struct regmap *map) { - const u8 config[] = { + static const u8 config[] = { PM886_GPADC_CONFIG1_EN_ALL, PM886_GPADC_CONFIG2_EN_ALL, PM886_GPADC_GND_DET2_EN, diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index c6d3cf68b1f7..415e519ad4eb 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -33,7 +33,8 @@ config AB8500_GPADC and USB voltages integral to the U8500 platform. config AD_SIGMA_DELTA - tristate + tristate "Analog Devices Sigma-Delta Modulator support" if COMPILE_TEST + depends on SPI select IIO_BUFFER select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER @@ -945,6 +946,25 @@ config LTC2309 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 @@ -1167,6 +1187,17 @@ config MCP3911 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 @@ -1791,6 +1822,18 @@ config TI_ADS1119 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 @@ -1966,6 +2009,39 @@ config TWL6030_GPADC 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 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 707dd708912f..dcec0abb03b7 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -81,6 +81,7 @@ 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 @@ -100,6 +101,7 @@ 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 @@ -153,6 +155,7 @@ 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 @@ -168,6 +171,9 @@ 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 diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c index c2f6f5f9812c..a33896b2cf97 100644 --- a/drivers/iio/adc/ad4000.c +++ b/drivers/iio/adc/ad4000.c @@ -1176,37 +1176,37 @@ static int ad4000_probe(struct spi_device *spi) } static const struct spi_device_id ad4000_id[] = { - { "ad4000", (kernel_ulong_t)&ad4000_chip_info }, - { "ad4001", (kernel_ulong_t)&ad4001_chip_info }, - { "ad4002", (kernel_ulong_t)&ad4002_chip_info }, - { "ad4003", (kernel_ulong_t)&ad4003_chip_info }, - { "ad4004", (kernel_ulong_t)&ad4004_chip_info }, - { "ad4005", (kernel_ulong_t)&ad4005_chip_info }, - { "ad4006", (kernel_ulong_t)&ad4006_chip_info }, - { "ad4007", (kernel_ulong_t)&ad4007_chip_info }, - { "ad4008", (kernel_ulong_t)&ad4008_chip_info }, - { "ad4010", (kernel_ulong_t)&ad4010_chip_info }, - { "ad4011", (kernel_ulong_t)&ad4011_chip_info }, - { "ad4020", (kernel_ulong_t)&ad4020_chip_info }, - { "ad4021", (kernel_ulong_t)&ad4021_chip_info }, - { "ad4022", (kernel_ulong_t)&ad4022_chip_info }, - { "adaq4001", (kernel_ulong_t)&adaq4001_chip_info }, - { "adaq4003", (kernel_ulong_t)&adaq4003_chip_info }, - { "ad7685", (kernel_ulong_t)&ad7685_chip_info }, - { "ad7686", (kernel_ulong_t)&ad7686_chip_info }, - { "ad7687", (kernel_ulong_t)&ad7687_chip_info }, - { "ad7688", (kernel_ulong_t)&ad7688_chip_info }, - { "ad7690", (kernel_ulong_t)&ad7690_chip_info }, - { "ad7691", (kernel_ulong_t)&ad7691_chip_info }, - { "ad7693", (kernel_ulong_t)&ad7693_chip_info }, - { "ad7942", (kernel_ulong_t)&ad7942_chip_info }, - { "ad7946", (kernel_ulong_t)&ad7946_chip_info }, - { "ad7980", (kernel_ulong_t)&ad7980_chip_info }, - { "ad7982", (kernel_ulong_t)&ad7982_chip_info }, - { "ad7983", (kernel_ulong_t)&ad7983_chip_info }, - { "ad7984", (kernel_ulong_t)&ad7984_chip_info }, - { "ad7988-1", (kernel_ulong_t)&ad7988_1_chip_info }, - { "ad7988-5", (kernel_ulong_t)&ad7988_5_chip_info }, + { .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); diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c index 9c5f19321e3b..e97400a1aa48 100644 --- a/drivers/iio/adc/ad4030.c +++ b/drivers/iio/adc/ad4030.c @@ -1763,13 +1763,13 @@ static const struct ad4030_chip_info adaq4224_chip_info = { }; static const struct spi_device_id ad4030_id_table[] = { - { "ad4030-24", (kernel_ulong_t)&ad4030_24_chip_info }, - { "ad4630-16", (kernel_ulong_t)&ad4630_16_chip_info }, - { "ad4630-24", (kernel_ulong_t)&ad4630_24_chip_info }, - { "ad4632-16", (kernel_ulong_t)&ad4632_16_chip_info }, - { "ad4632-24", (kernel_ulong_t)&ad4632_24_chip_info }, - { "adaq4216", (kernel_ulong_t)&adaq4216_chip_info }, - { "adaq4224", (kernel_ulong_t)&adaq4224_chip_info }, + { .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); diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 967144eee3eb..04cd6628ebff 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -135,6 +135,7 @@ #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 @@ -541,6 +542,11 @@ static const struct iio_chan_spec ad4880_channels[] = { 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), @@ -646,6 +652,16 @@ static const struct ad4080_chip_info ad4880_chip_info = { .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, @@ -853,17 +869,18 @@ static int ad4080_probe(struct spi_device *spi) } static const struct spi_device_id ad4080_id[] = { - { "ad4080", (kernel_ulong_t)&ad4080_chip_info }, - { "ad4081", (kernel_ulong_t)&ad4081_chip_info }, - { "ad4082", (kernel_ulong_t)&ad4082_chip_info }, - { "ad4083", (kernel_ulong_t)&ad4083_chip_info }, - { "ad4084", (kernel_ulong_t)&ad4084_chip_info }, - { "ad4085", (kernel_ulong_t)&ad4085_chip_info }, - { "ad4086", (kernel_ulong_t)&ad4086_chip_info }, - { "ad4087", (kernel_ulong_t)&ad4087_chip_info }, - { "ad4088", (kernel_ulong_t)&ad4088_chip_info }, - { "ad4880", (kernel_ulong_t)&ad4880_chip_info }, - { "ad4884", (kernel_ulong_t)&ad4884_chip_info }, + { .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); @@ -879,6 +896,7 @@ static const struct of_device_id ad4080_of_match[] = { { .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 }, { } }; diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index 7f39f3484062..3ffc4b765e0e 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -2425,12 +2425,12 @@ static const struct of_device_id ad4130_of_match[] = { MODULE_DEVICE_TABLE(of, ad4130_of_match); static const struct spi_device_id ad4130_id_table[] = { - { "ad4129-4", (kernel_ulong_t)&ad4129_4_chip_info }, - { "ad4129-8", (kernel_ulong_t)&ad4129_8_chip_info }, - { "ad4130-4", (kernel_ulong_t)&ad4130_4_chip_info }, - { "ad4130", (kernel_ulong_t)&ad4130_8_chip_info }, - { "ad4131-4", (kernel_ulong_t)&ad4131_4_chip_info }, - { "ad4131-8", (kernel_ulong_t)&ad4131_8_chip_info }, + { .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); diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c index cb9d74316f6a..0490218bb0e9 100644 --- a/drivers/iio/adc/ad4134.c +++ b/drivers/iio/adc/ad4134.c @@ -472,7 +472,7 @@ static int ad4134_probe(struct spi_device *spi) } static const struct spi_device_id ad4134_id[] = { - { "ad4134" }, + { .name = "ad4134" }, { } }; MODULE_DEVICE_TABLE(spi, ad4134_id); diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c index 627cbf5a37b0..f7757b3a931c 100644 --- a/drivers/iio/adc/ad4170-4.c +++ b/drivers/iio/adc/ad4170-4.c @@ -2979,9 +2979,9 @@ static int ad4170_probe(struct spi_device *spi) } static const struct spi_device_id ad4170_id_table[] = { - { "ad4170-4", (kernel_ulong_t)&ad4170_chip_info }, - { "ad4190-4", (kernel_ulong_t)&ad4190_chip_info }, - { "ad4195-4", (kernel_ulong_t)&ad4195_chip_info }, + { .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); diff --git a/drivers/iio/adc/ad4851.c b/drivers/iio/adc/ad4851.c index 940b042d743a..480d1798f17b 100644 --- a/drivers/iio/adc/ad4851.c +++ b/drivers/iio/adc/ad4851.c @@ -1285,15 +1285,15 @@ static const struct of_device_id ad4851_of_match[] = { }; static const struct spi_device_id ad4851_spi_id[] = { - { "ad4851", (kernel_ulong_t)&ad4851_info }, - { "ad4852", (kernel_ulong_t)&ad4852_info }, - { "ad4853", (kernel_ulong_t)&ad4853_info }, - { "ad4854", (kernel_ulong_t)&ad4854_info }, - { "ad4855", (kernel_ulong_t)&ad4855_info }, - { "ad4856", (kernel_ulong_t)&ad4856_info }, - { "ad4857", (kernel_ulong_t)&ad4857_info }, - { "ad4858", (kernel_ulong_t)&ad4858_info }, - { "ad4858i", (kernel_ulong_t)&ad4858i_info }, + { .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); diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c index e93b8bb60e8e..465a8ba01a22 100644 --- a/drivers/iio/adc/ad7091r8.c +++ b/drivers/iio/adc/ad7091r8.c @@ -249,9 +249,9 @@ static const struct of_device_id ad7091r8_of_match[] = { MODULE_DEVICE_TABLE(of, ad7091r8_of_match); static const struct spi_device_id ad7091r8_spi_id[] = { - { "ad7091r2", (kernel_ulong_t)&ad7091r2_init_info }, - { "ad7091r4", (kernel_ulong_t)&ad7091r4_init_info }, - { "ad7091r8", (kernel_ulong_t)&ad7091r8_init_info }, + { .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); diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 19058d081418..fc5dbc624856 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -1683,8 +1683,8 @@ static const struct of_device_id ad7124_of_match[] = { MODULE_DEVICE_TABLE(of, ad7124_of_match); static const struct spi_device_id ad71124_ids[] = { - { "ad7124-4", (kernel_ulong_t)&ad7124_4_chip_info }, - { "ad7124-8", (kernel_ulong_t)&ad7124_8_chip_info }, + { .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); diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index f78e758706ed..eb47175a0528 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -2065,19 +2065,19 @@ static const struct of_device_id ad7173_of_match[] = { MODULE_DEVICE_TABLE(of, ad7173_of_match); static const struct spi_device_id ad7173_id_table[] = { - { "ad4111", (kernel_ulong_t)&ad4111_device_info }, - { "ad4112", (kernel_ulong_t)&ad4112_device_info }, - { "ad4113", (kernel_ulong_t)&ad4113_device_info }, - { "ad4114", (kernel_ulong_t)&ad4114_device_info }, - { "ad4115", (kernel_ulong_t)&ad4115_device_info }, - { "ad4116", (kernel_ulong_t)&ad4116_device_info }, - { "ad7172-2", (kernel_ulong_t)&ad7172_2_device_info }, - { "ad7172-4", (kernel_ulong_t)&ad7172_4_device_info }, - { "ad7173-8", (kernel_ulong_t)&ad7173_8_device_info }, - { "ad7175-2", (kernel_ulong_t)&ad7175_2_device_info }, - { "ad7175-8", (kernel_ulong_t)&ad7175_8_device_info }, - { "ad7176-2", (kernel_ulong_t)&ad7176_2_device_info }, - { "ad7177-2", (kernel_ulong_t)&ad7177_2_device_info }, + { .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); diff --git a/drivers/iio/adc/ad7191.c b/drivers/iio/adc/ad7191.c index 94b172dce866..071d26191b3c 100644 --- a/drivers/iio/adc/ad7191.c +++ b/drivers/iio/adc/ad7191.c @@ -535,7 +535,7 @@ static const struct of_device_id ad7191_of_match[] = { MODULE_DEVICE_TABLE(of, ad7191_of_match); static const struct spi_device_id ad7191_id_table[] = { - { "ad7191" }, + { .name = "ad7191" }, { } }; MODULE_DEVICE_TABLE(spi, ad7191_id_table); diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 2cfad0bda752..d8f5886fcf69 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -1448,11 +1448,11 @@ static const struct of_device_id ad7192_of_match[] = { MODULE_DEVICE_TABLE(of, ad7192_of_match); static const struct spi_device_id ad7192_ids[] = { - { "ad7190", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] }, - { "ad7192", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] }, - { "ad7193", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] }, - { "ad7194", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] }, - { "ad7195", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] }, + { .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); diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 0ef36c249ab8..594a070c3745 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -453,8 +453,8 @@ static int ad7266_probe(struct spi_device *spi) } static const struct spi_device_id ad7266_id[] = { - { "ad7265", 0 }, - { "ad7266", 0 }, + { .name = "ad7265" }, + { .name = "ad7266" }, { } }; MODULE_DEVICE_TABLE(spi, ad7266_id); diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c index 2972e706de92..505fa68ee539 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -1082,7 +1082,7 @@ static int ad7280_probe(struct spi_device *spi) } static const struct spi_device_id ad7280_id[] = { - { "ad7280a", 0 }, + { .name = "ad7280a" }, { } }; MODULE_DEVICE_TABLE(spi, ad7280_id); diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index 0ba0fbad4f70..1d7bf46b80e2 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -299,7 +299,7 @@ static int ad7292_probe(struct spi_device *spi) } static const struct spi_device_id ad7292_id_table[] = { - { "ad7292", 0 }, + { .name = "ad7292" }, { } }; MODULE_DEVICE_TABLE(spi, ad7292_id_table); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 5fa76aec360e..c669022b5dec 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -353,7 +353,7 @@ static const struct acpi_device_id ad7298_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids); static const struct spi_device_id ad7298_id[] = { - { "ad7298", 0 }, + { .name = "ad7298" }, { } }; MODULE_DEVICE_TABLE(spi, ad7298_id); diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index 9f77990a03f9..2e90f125a797 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -2116,24 +2116,24 @@ static const struct of_device_id ad7380_of_match_table[] = { }; static const struct spi_device_id ad7380_id_table[] = { - { "ad7380", (kernel_ulong_t)&ad7380_chip_info }, - { "ad7381", (kernel_ulong_t)&ad7381_chip_info }, - { "ad7383", (kernel_ulong_t)&ad7383_chip_info }, - { "ad7384", (kernel_ulong_t)&ad7384_chip_info }, - { "ad7386", (kernel_ulong_t)&ad7386_chip_info }, - { "ad7387", (kernel_ulong_t)&ad7387_chip_info }, - { "ad7388", (kernel_ulong_t)&ad7388_chip_info }, - { "ad7380-4", (kernel_ulong_t)&ad7380_4_chip_info }, - { "ad7381-4", (kernel_ulong_t)&ad7381_4_chip_info }, - { "ad7383-4", (kernel_ulong_t)&ad7383_4_chip_info }, - { "ad7384-4", (kernel_ulong_t)&ad7384_4_chip_info }, - { "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info }, - { "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info }, - { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info }, - { "ad7389-4", (kernel_ulong_t)&ad7389_4_chip_info }, - { "adaq4370-4", (kernel_ulong_t)&adaq4370_4_chip_info }, - { "adaq4380-4", (kernel_ulong_t)&adaq4380_4_chip_info }, - { "adaq4381-4", (kernel_ulong_t)&adaq4381_4_chip_info }, + { .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); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 21d3f6aae972..c007f453ae18 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -411,42 +411,42 @@ static int ad7476_probe(struct spi_device *spi) } static const struct spi_device_id ad7476_id[] = { - { "ad7091", (kernel_ulong_t)&ad7091_chip_info }, - { "ad7091r", (kernel_ulong_t)&ad7091r_chip_info }, - { "ad7273", (kernel_ulong_t)&ad7273_chip_info }, - { "ad7274", (kernel_ulong_t)&ad7274_chip_info }, - { "ad7276", (kernel_ulong_t)&ad7276_chip_info }, - { "ad7277", (kernel_ulong_t)&ad7277_chip_info }, - { "ad7278", (kernel_ulong_t)&ad7278_chip_info }, - { "ad7466", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7467", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7468", (kernel_ulong_t)&ad7468_chip_info }, - { "ad7475", (kernel_ulong_t)&ad7475_chip_info }, - { "ad7476", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7476a", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7477", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7477a", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7478", (kernel_ulong_t)&ad7468_chip_info }, - { "ad7478a", (kernel_ulong_t)&ad7468_chip_info }, - { "ad7495", (kernel_ulong_t)&ad7495_chip_info }, - { "ad7910", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7920", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7940", (kernel_ulong_t)&ad7940_chip_info }, - { "adc081s", (kernel_ulong_t)&adc081s_chip_info }, - { "adc101s", (kernel_ulong_t)&adc101s_chip_info }, - { "adc121s", (kernel_ulong_t)&adc121s_chip_info }, - { "ads7866", (kernel_ulong_t)&ads7866_chip_info }, - { "ads7867", (kernel_ulong_t)&ads7867_chip_info }, - { "ads7868", (kernel_ulong_t)&ads7868_chip_info }, - { "bd79105", (kernel_ulong_t)&bd79105_chip_info }, + { .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. */ - { "bu79100g", (kernel_ulong_t)&ads7866_chip_info }, - { "ltc2314-14", (kernel_ulong_t)<c2314_14_chip_info }, + { .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); diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index 7e17ccbcedd0..1ebdfb8580ab 100644 --- a/drivers/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -463,17 +463,17 @@ static int ad7606_spi_probe(struct spi_device *spi) } static const struct spi_device_id ad7606_id_table[] = { - { "ad7605-4", (kernel_ulong_t)&ad7605_4_bus_info }, - { "ad7606-4", (kernel_ulong_t)&ad7606_4_bus_info }, - { "ad7606-6", (kernel_ulong_t)&ad7606_6_bus_info }, - { "ad7606-8", (kernel_ulong_t)&ad7606_8_bus_info }, - { "ad7606b", (kernel_ulong_t)&ad7606b_bus_info }, - { "ad7606c-16", (kernel_ulong_t)&ad7606c_16_bus_info }, - { "ad7606c-18", (kernel_ulong_t)&ad7606c_18_bus_info }, - { "ad7607", (kernel_ulong_t)&ad7607_bus_info }, - { "ad7608", (kernel_ulong_t)&ad7608_bus_info }, - { "ad7609", (kernel_ulong_t)&ad7609_bus_info }, - { "ad7616", (kernel_ulong_t)&ad7616_bus_info }, + { .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); diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index 9e4a66477d2d..92957196de59 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -285,12 +285,12 @@ static int ad7766_probe(struct spi_device *spi) } static const struct spi_device_id ad7766_id[] = { - { "ad7766", ID_AD7766 }, - { "ad7766-1", ID_AD7766_1 }, - { "ad7766-2", ID_AD7766_2 }, - { "ad7767", ID_AD7766 }, - { "ad7767-1", ID_AD7766_1 }, - { "ad7767-2", ID_AD7766_2 }, + { .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); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 598936e47fd2..e9060c1bbe6f 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -1918,10 +1918,10 @@ static int ad7768_probe(struct spi_device *spi) } static const struct spi_device_id ad7768_id_table[] = { - { "ad7768-1", (kernel_ulong_t)&ad7768_chip_info }, - { "adaq7767-1", (kernel_ulong_t)&adaq7767_chip_info }, - { "adaq7768-1", (kernel_ulong_t)&adaq7768_chip_info }, - { "adaq7769-1", (kernel_ulong_t)&adaq7769_chip_info }, + { .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); diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c index 003e23d6e242..79fb38d0f400 100644 --- a/drivers/iio/adc/ad7779.c +++ b/drivers/iio/adc/ad7779.c @@ -142,7 +142,6 @@ struct ad7779_state { const struct ad7779_chip_info *chip_info; struct clk *mclk; struct iio_trigger *trig; - struct completion completion; unsigned int sampling_freq; enum ad7779_filter filter_enabled; struct iio_backend *back; @@ -842,8 +841,7 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name, st->trig); if (ret) - return dev_err_probe(dev, ret, "request IRQ %d failed\n", - st->spi->irq); + return ret; ret = devm_iio_trigger_register(dev, st->trig); if (ret) @@ -851,8 +849,6 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev indio_dev->trig = iio_trigger_get(st->trig); - init_completion(&st->completion); - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, &iio_pollfunc_store_time, &ad7779_trigger_handler, diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index 26382b1f8c9e..1cfad70320fe 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -347,10 +347,10 @@ static int ad7780_probe(struct spi_device *spi) } static const struct spi_device_id ad7780_id[] = { - { "ad7170", ID_AD7170 }, - { "ad7171", ID_AD7171 }, - { "ad7780", ID_AD7780 }, - { "ad7781", ID_AD7781 }, + { .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); diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index bcdc19e799aa..c2688307bbba 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -458,11 +458,11 @@ static int ad7791_probe(struct spi_device *spi) } static const struct spi_device_id ad7791_spi_ids[] = { - { "ad7787", AD7787 }, - { "ad7788", AD7788 }, - { "ad7789", AD7789 }, - { "ad7790", AD7790 }, - { "ad7791", AD7791 }, + { .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); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index ea1c192e2142..b0f44c144f20 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -829,15 +829,15 @@ static int ad7793_probe(struct spi_device *spi) } static const struct spi_device_id ad7793_id[] = { - { "ad7785", ID_AD7785 }, - { "ad7792", ID_AD7792 }, - { "ad7793", ID_AD7793 }, - { "ad7794", ID_AD7794 }, - { "ad7795", ID_AD7795 }, - { "ad7796", ID_AD7796 }, - { "ad7797", ID_AD7797 }, - { "ad7798", ID_AD7798 }, - { "ad7799", ID_AD7799 }, + { .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); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 068171d54596..62aaedbce748 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -328,7 +328,7 @@ static int ad7887_probe(struct spi_device *spi) } static const struct spi_device_id ad7887_id[] = { - { "ad7887", ID_AD7887 }, + { .name = "ad7887", .driver_data = ID_AD7887 }, { } }; MODULE_DEVICE_TABLE(spi, ad7887_id); diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index acc87d486aa4..44e014da9722 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -360,13 +360,13 @@ static int ad7923_probe(struct spi_device *spi) } static const struct spi_device_id ad7923_id[] = { - { "ad7904", AD7904 }, - { "ad7914", AD7914 }, - { "ad7923", AD7924 }, - { "ad7924", AD7924 }, - { "ad7908", AD7908 }, - { "ad7918", AD7918 }, - { "ad7928", AD7928 }, + { .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); diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c index 7722cf9e8214..12656d3cdcec 100644 --- a/drivers/iio/adc/ad7944.c +++ b/drivers/iio/adc/ad7944.c @@ -865,9 +865,9 @@ static const struct of_device_id ad7944_of_match[] = { MODULE_DEVICE_TABLE(of, ad7944_of_match); static const struct spi_device_id ad7944_spi_id[] = { - { "ad7944", (kernel_ulong_t)&ad7944_chip_info }, - { "ad7985", (kernel_ulong_t)&ad7985_chip_info }, - { "ad7986", (kernel_ulong_t)&ad7986_chip_info }, + { .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 }, { } }; diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index ebc629bcfd4d..93a57a3d59b8 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -410,9 +410,9 @@ static const struct of_device_id ad7949_spi_of_id[] = { MODULE_DEVICE_TABLE(of, ad7949_spi_of_id); static const struct spi_device_id ad7949_spi_id[] = { - { "ad7949", ID_AD7949 }, - { "ad7682", ID_AD7682 }, - { "ad7689", ID_AD7689 }, + { .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); diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 0c377f9a7f25..34b7b1256f0d 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -1406,13 +1406,13 @@ static const struct of_device_id ad9467_of_match[] = { MODULE_DEVICE_TABLE(of, ad9467_of_match); static const struct spi_device_id ad9467_ids[] = { - { "ad9211", (kernel_ulong_t)&ad9211_chip_tbl }, - { "ad9265", (kernel_ulong_t)&ad9265_chip_tbl }, - { "ad9434", (kernel_ulong_t)&ad9434_chip_tbl }, - { "ad9467", (kernel_ulong_t)&ad9467_chip_tbl }, - { "ad9643", (kernel_ulong_t)&ad9643_chip_tbl }, - { "ad9649", (kernel_ulong_t)&ad9649_chip_tbl, }, - { "ad9652", (kernel_ulong_t)&ad9652_chip_tbl, }, + { .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); diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index e57e24b6acdf..da6caabfe2a4 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * ADE9000 driver * * Copyright 2025 Analog Devices Inc. @@ -1769,7 +1769,7 @@ static int ade9000_probe(struct spi_device *spi) }; static const struct spi_device_id ade9000_id[] = { - { "ade9000", 0 }, + { .name = "ade9000" }, { } }; MODULE_DEVICE_TABLE(spi, ade9000_id); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index f610ad729bf3..0ac74ad92fca 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -988,7 +988,7 @@ static int at91_adc_probe(struct platform_device *pdev) struct iio_dev *idev; struct at91_adc_state *st; u32 reg, prop; - char *s; + const char *s; idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state)); if (!idev) @@ -1023,7 +1023,7 @@ static int at91_adc_probe(struct platform_device *pdev) st->res = st->caps->high_res_bits; if (st->caps->low_res_bits && - !of_property_read_string(node, "atmel,adc-use-res", (const char **)&s) + !of_property_read_string(node, "atmel,adc-use-res", &s) && !strcmp(s, "lowres")) st->res = st->caps->low_res_bits; diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c index 30672e584c10..aace7e103770 100644 --- a/drivers/iio/adc/envelope-detector.c +++ b/drivers/iio/adc/envelope-detector.c @@ -405,3 +405,4 @@ module_platform_driver(envelope_detector_driver); MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator"); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index b01ba86c2945..9fa63de4b9ca 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -526,7 +526,7 @@ static const struct of_device_id hi8435_dt_ids[] = { MODULE_DEVICE_TABLE(of, hi8435_dt_ids); static const struct spi_device_id hi8435_id[] = { - { "hi8435", 0 }, + { .name = "hi8435" }, { } }; MODULE_DEVICE_TABLE(spi, hi8435_id); diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 17e9badf1d1c..00adafa7cbd7 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -4,6 +4,8 @@ * * Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de> */ +#include <linux/array_size.h> +#include <linux/dev_printk.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> @@ -20,6 +22,8 @@ #include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> +#define HX711_DATA_BITS 24 + /* gain to pulse and scale conversion */ #define HX711_GAIN_MAX 3 #define HX711_RESET_GAIN 128 @@ -73,6 +77,20 @@ static int hx711_get_scale_to_gain(const int *gain_scale, int scale) 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; @@ -80,6 +98,7 @@ struct hx711_data { 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 @@ -137,17 +156,18 @@ static int hx711_cycle(struct hx711_data *hx711_data) return gpiod_get_value(hx711_data->gpiod_dout); } -static int hx711_read(struct hx711_data *hx711_data) +static int hx711_read(struct hx711_data *hx711_data, int trailing_pulses) { - int i, ret; int value = 0; - int val = gpiod_get_value(hx711_data->gpiod_dout); + 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 (i = 0; i < 24; i++) { + for (int i = 0; i < HX711_DATA_BITS; i++) { value <<= 1; ret = hx711_cycle(hx711_data); if (ret) @@ -156,7 +176,7 @@ static int hx711_read(struct hx711_data *hx711_data) value ^= 0x800000; - for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++) + for (int i = 0; i < trailing_pulses; i++) hx711_cycle(hx711_data); return value; @@ -186,8 +206,9 @@ static int hx711_wait_for_ready(struct hx711_data *hx711_data) static int hx711_reset(struct hx711_data *hx711_data) { - int val = hx711_wait_for_ready(hx711_data); + int val; + val = hx711_wait_for_ready(hx711_data); if (val) { /* * an examination with the oszilloscope indicated @@ -219,7 +240,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) if (hx711_data->gain_set == 32) { hx711_data->gain_set = hx711_data->gain_chan_a; - ret = hx711_read(hx711_data); + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); if (ret < 0) return ret; @@ -231,7 +253,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) if (hx711_data->gain_set != 32) { hx711_data->gain_set = 32; - ret = hx711_read(hx711_data); + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); if (ret < 0) return ret; @@ -244,10 +267,26 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) return 0; } -static int hx711_reset_read(struct hx711_data *hx711_data, int chan) +static int hx711_set_hx711_channel(struct hx711_data *hx711_data, + const struct iio_chan_spec *chan, + int *trailing_pulses) { int ret; - int val; + + 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 @@ -258,13 +297,11 @@ static int hx711_reset_read(struct hx711_data *hx711_data, int chan) return -EIO; } - ret = hx711_set_gain_for_channel(hx711_data, chan); + ret = hx711_set_hx711_channel(hx711_data, chan, &trailing_pulses); if (ret < 0) return ret; - val = hx711_read(hx711_data); - - return val; + return hx711_read(hx711_data, trailing_pulses); } static int hx711_read_raw(struct iio_dev *indio_dev, @@ -277,7 +314,7 @@ static int hx711_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: mutex_lock(&hx711_data->lock); - *val = hx711_reset_read(hx711_data, chan->channel); + *val = hx711_reset_read(hx711_data, chan); mutex_unlock(&hx711_data->lock); @@ -331,7 +368,8 @@ static int hx711_write_raw(struct iio_dev *indio_dev, if (gain != 32) hx711_data->gain_chan_a = gain; - ret = hx711_read(hx711_data); + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); if (ret < 0) { mutex_unlock(&hx711_data->lock); return ret; @@ -366,8 +404,8 @@ static irqreturn_t hx711_trigger(int irq, void *p) 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].channel); + hx711_data->buffer.channel[j] = + hx711_reset_read(hx711_data, &indio_dev->channels[i]); j++; } @@ -455,8 +493,16 @@ static const struct iio_chan_spec hx711_chan_spec[] = { 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; @@ -472,6 +518,12 @@ static int hx711_probe(struct platform_device *pdev) 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 @@ -533,11 +585,11 @@ static int hx711_probe(struct platform_device *pdev) hx711_data->data_ready_delay_ns = 1000000000 / hx711_data->clock_frequency; - indio_dev->name = "hx711"; - indio_dev->info = &hx711_iio_info; + indio_dev->name = chip_info->name; + indio_dev->info = chip_info->iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = hx711_chan_spec; - indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec); + 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, @@ -554,7 +606,7 @@ static int hx711_probe(struct platform_device *pdev) } static const struct of_device_id of_hx711_match[] = { - { .compatible = "avia,hx711", }, + { .compatible = "avia,hx711", .data = &hx711_chip }, { } }; diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c new file mode 100644 index 000000000000..b0ff2be1aee0 --- /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 <marcelo.schmitt@analog.com> + */ + +#include <linux/array_size.h> +#include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/byteorder/generic.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/device-id/spi.h> +#include <linux/device-id/of.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/math64.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/pwm.h> +#include <linux/spi/spi.h> +#include <linux/spi/offload/consumer.h> +#include <linux/spi/offload/types.h> +#include <linux/time64.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/buffer-dmaengine.h> +#include <linux/iio/iio.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/types.h> + +#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 <marcelo.schmitt@analog.com>"); +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/max1027.c b/drivers/iio/adc/max1027.c index 8b2c7b2a9c8f..fb6f12316724 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -72,12 +72,12 @@ enum max1027_id { }; static const struct spi_device_id max1027_id[] = { - { "max1027", max1027 }, - { "max1029", max1029 }, - { "max1031", max1031 }, - { "max1227", max1227 }, - { "max1229", max1229 }, - { "max1231", max1231 }, + { .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); diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index d394e03cc4e8..75e51ce3077e 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -256,9 +256,9 @@ static int max1118_probe(struct spi_device *spi) } static const struct spi_device_id max1118_id[] = { - { "max1117", max1117 }, - { "max1118", max1118 }, - { "max1119", max1119 }, + { .name = "max1117", .driver_data = max1117 }, + { .name = "max1118", .driver_data = max1118 }, + { .name = "max1119", .driver_data = max1119 }, { } }; MODULE_DEVICE_TABLE(spi, max1118_id); diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c index 6c803df220b6..63b6561dcf47 100644 --- a/drivers/iio/adc/max11205.c +++ b/drivers/iio/adc/max11205.c @@ -145,8 +145,8 @@ static int max11205_probe(struct spi_device *spi) } static const struct spi_device_id max11205_spi_ids[] = { - { "max11205a", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] }, - { "max11205b", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] }, + { .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); diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index dc1b96356592..23ae684cf08b 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -1025,7 +1025,7 @@ static const struct of_device_id max11410_spi_of_id[] = { MODULE_DEVICE_TABLE(of, max11410_spi_of_id); static const struct spi_device_id max11410_id[] = { - { "max11410" }, + { .name = "max11410" }, { } }; MODULE_DEVICE_TABLE(spi, max11410_id); diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c index d62c1a011659..b3cecbf9a89e 100644 --- a/drivers/iio/adc/max1241.c +++ b/drivers/iio/adc/max1241.c @@ -15,10 +15,6 @@ #define MAX1241_VAL_MASK GENMASK(11, 0) #define MAX1241_SHUTDOWN_DELAY_USEC 4 -enum max1241_id { - max1241, -}; - struct max1241 { struct spi_device *spi; struct mutex lock; @@ -166,7 +162,7 @@ static int max1241_probe(struct spi_device *spi) else dev_dbg(dev, "no shutdown pin passed, low-power mode disabled"); - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->name = "max1241"; indio_dev->info = &max1241_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = max1241_channels; @@ -176,9 +172,10 @@ static int max1241_probe(struct spi_device *spi) } static const struct spi_device_id max1241_id[] = { - { "max1241", max1241 }, + { .name = "max1241" }, { } }; +MODULE_DEVICE_TABLE(spi, max1241_id); static const struct of_device_id max1241_dt_ids[] = { { .compatible = "maxim,max1241" }, diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c index 09017163b191..58adeac62dde 100644 --- a/drivers/iio/adc/max14001.c +++ b/drivers/iio/adc/max14001.c @@ -362,8 +362,8 @@ static struct max14001_chip_info max14002_chip_info = { }; static const struct spi_device_id max14001_id_table[] = { - { "max14001", (kernel_ulong_t)&max14001_chip_info }, - { "max14002", (kernel_ulong_t)&max14002_chip_info }, + { .name = "max14001", .driver_data = (kernel_ulong_t)&max14001_chip_info }, + { .name = "max14002", .driver_data = (kernel_ulong_t)&max14002_chip_info }, { } }; diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index 686c519aa3d2..18cb957e7095 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -476,19 +476,19 @@ static const struct of_device_id mcp320x_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp320x_dt_ids); static const struct spi_device_id mcp320x_id[] = { - { "mcp3001", mcp3001 }, - { "mcp3002", mcp3002 }, - { "mcp3004", mcp3004 }, - { "mcp3008", mcp3008 }, - { "mcp3201", mcp3201 }, - { "mcp3202", mcp3202 }, - { "mcp3204", mcp3204 }, - { "mcp3208", mcp3208 }, - { "mcp3301", mcp3301 }, - { "mcp3550-50", mcp3550_50 }, - { "mcp3550-60", mcp3550_60 }, - { "mcp3551", mcp3551 }, - { "mcp3553", mcp3553 }, + { .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); diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c index 36675563829e..259ac4ebdedf 100644 --- a/drivers/iio/adc/mcp3564.c +++ b/drivers/iio/adc/mcp3564.c @@ -1451,18 +1451,18 @@ static const struct of_device_id mcp3564_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp3564_dt_ids); static const struct spi_device_id mcp3564_id[] = { - { "mcp3461", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] }, - { "mcp3462", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] }, - { "mcp3464", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] }, - { "mcp3561", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] }, - { "mcp3562", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] }, - { "mcp3564", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] }, - { "mcp3461r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] }, - { "mcp3462r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] }, - { "mcp3464r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] }, - { "mcp3561r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] }, - { "mcp3562r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] }, - { "mcp3564r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] }, + { .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); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 5bf74b49bdb5..797424f59f74 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -927,13 +927,13 @@ static const struct of_device_id mcp3911_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp3911_dt_ids); static const struct spi_device_id mcp3911_id[] = { - { "mcp3910", (kernel_ulong_t)&mcp3911_chip_info[MCP3910] }, - { "mcp3911", (kernel_ulong_t)&mcp3911_chip_info[MCP3911] }, - { "mcp3912", (kernel_ulong_t)&mcp3911_chip_info[MCP3912] }, - { "mcp3913", (kernel_ulong_t)&mcp3911_chip_info[MCP3913] }, - { "mcp3914", (kernel_ulong_t)&mcp3911_chip_info[MCP3914] }, - { "mcp3918", (kernel_ulong_t)&mcp3911_chip_info[MCP3918] }, - { "mcp3919", (kernel_ulong_t)&mcp3911_chip_info[MCP3919] }, + { .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); diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c new file mode 100644 index 000000000000..4f73ac42abb7 --- /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 <rva333@protonmail.com> + * + * Based on drivers/iio/adc/mt6359-auxadc.c + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/stringify.h> +#include <linux/time.h> +#include <linux/types.h> + +#include <linux/mfd/mt6323/registers.h> + +#include <dt-bindings/iio/adc/mediatek,mt6323-auxadc.h> + +#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/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 3a17b3898bf6..d38cf3caeae0 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -159,16 +159,6 @@ static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = { RCAR_GYROADC_CHAN(7), }; -static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on) -{ - struct device *dev = priv->dev; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -200,7 +190,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, if (!iio_device_claim_direct(indio_dev)) return -EBUSY; - ret = rcar_gyroadc_set_power(priv, true); + ret = pm_runtime_resume_and_get(priv->dev); if (ret < 0) { iio_device_release_direct(indio_dev); return ret; @@ -209,7 +199,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, *val = readl(priv->regs + datareg); *val &= BIT(priv->sample_width) - 1; - ret = rcar_gyroadc_set_power(priv, false); + ret = pm_runtime_put_autosuspend(priv->dev); iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/rohm-bd79112.c b/drivers/iio/adc/rohm-bd79112.c index c4b0bec80794..2506fec7be84 100644 --- a/drivers/iio/adc/rohm-bd79112.c +++ b/drivers/iio/adc/rohm-bd79112.c @@ -529,7 +529,7 @@ static const struct of_device_id bd79112_of_match[] = { MODULE_DEVICE_TABLE(of, bd79112_of_match); static const struct spi_device_id bd79112_id[] = { - { "bd79112" }, + { .name = "bd79112" }, { } }; MODULE_DEVICE_TABLE(spi, bd79112_id); diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index ba525a3c5cc2..2083c4351e43 100644 --- a/drivers/iio/adc/rtq6056.c +++ b/drivers/iio/adc/rtq6056.c @@ -871,8 +871,8 @@ static const struct richtek_dev_data rtq6059_devdata = { }; static const struct i2c_device_id rtq6056_id[] = { - { "rtq6056", (kernel_ulong_t)&rtq6056_devdata }, - { "rtq6059", (kernel_ulong_t)&rtq6059_devdata }, + { .name = "rtq6056", .driver_data = (kernel_ulong_t)&rtq6056_devdata }, + { .name = "rtq6059", .driver_data = (kernel_ulong_t)&rtq6059_devdata }, { } }; MODULE_DEVICE_TABLE(i2c, rtq6056_id); diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 9664b9bd75d4..00f05e167afc 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -660,11 +660,8 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, } df_ch->src = val; - ret = of_property_read_u32_index(indio_dev->dev.of_node, - "st,adc-alt-channel", chan_idx, - &df_ch->alt_si); - if (ret < 0) - df_ch->alt_si = 0; + df_ch->alt_si = of_property_present(indio_dev->dev.of_node, + "st,adc-alt-channel"); return 0; } @@ -1815,9 +1812,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev) adc->dfsdm->fl_list[adc->fl_id].ford = val; - ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val); - if (!ret) - adc->dfsdm->fl_list[adc->fl_id].sync_mode = 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); diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index e33a2f4bf66c..2df8f1c21c2a 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -189,10 +189,8 @@ static int adc081c_probe(struct i2c_client *client) err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL, adc081c_trigger_handler, NULL); - if (err < 0) { - dev_err(&client->dev, "iio triggered buffer setup failed\n"); - return err; - } + if (err < 0) + return dev_err_probe(&client->dev, err, "iio triggered buffer setup failed\n"); return devm_iio_device_register(&client->dev, iio); } diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c index 63d712d5d111..a522d8676034 100644 --- a/drivers/iio/adc/ti-adc0832.c +++ b/drivers/iio/adc/ti-adc0832.c @@ -313,10 +313,10 @@ static const struct of_device_id adc0832_dt_ids[] = { MODULE_DEVICE_TABLE(of, adc0832_dt_ids); static const struct spi_device_id adc0832_id[] = { - { "adc0831", adc0831 }, - { "adc0832", adc0832 }, - { "adc0834", adc0834 }, - { "adc0838", adc0838 }, + { .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); diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index 51596b500a90..cf2f6ee59411 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -228,10 +228,8 @@ static int adc084s021_probe(struct spi_device *spi) ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, adc084s021_buffer_trigger_handler, &adc084s021_buffer_setup_ops); - if (ret) { - dev_err(&spi->dev, "Failed to setup triggered buffer\n"); - return ret; - } + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to setup triggered buffer\n"); return devm_iio_device_register(&spi->dev, indio_dev); } @@ -243,7 +241,7 @@ static const struct of_device_id adc084s021_of_match[] = { MODULE_DEVICE_TABLE(of, adc084s021_of_match); static const struct spi_device_id adc084s021_id[] = { - { ADC084S021_DRIVER_NAME, 0 }, + { .name = ADC084S021_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(spi, adc084s021_id); diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c index d1f61b440959..10959e835cb8 100644 --- a/drivers/iio/adc/ti-adc108s102.c +++ b/drivers/iio/adc/ti-adc108s102.c @@ -276,7 +276,7 @@ static const struct acpi_device_id adc108s102_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids); static const struct spi_device_id adc108s102_id[] = { - { "adc108s102", 0 }, + { .name = "adc108s102" }, { } }; MODULE_DEVICE_TABLE(spi, adc108s102_id); diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c index e5ec4b073daa..279e4999eb49 100644 --- a/drivers/iio/adc/ti-adc12138.c +++ b/drivers/iio/adc/ti-adc12138.c @@ -516,9 +516,9 @@ static const struct of_device_id adc12138_dt_ids[] = { MODULE_DEVICE_TABLE(of, adc12138_dt_ids); static const struct spi_device_id adc12138_id[] = { - { "adc12130", adc12130 }, - { "adc12132", adc12132 }, - { "adc12138", adc12138 }, + { .name = "adc12130", .driver_data = adc12130 }, + { .name = "adc12132", .driver_data = adc12132 }, + { .name = "adc12138", .driver_data = adc12138 }, { } }; MODULE_DEVICE_TABLE(spi, adc12138_id); diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 2cb68582ee6a..1899813c1aee 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -247,18 +247,18 @@ static const struct of_device_id adc128_of_match[] = { MODULE_DEVICE_TABLE(of, adc128_of_match); static const struct spi_device_id adc128_id[] = { - { "adc128s052", (kernel_ulong_t)&adc128s_config }, - { "adc122s021", (kernel_ulong_t)&adc122s_config }, - { "adc122s051", (kernel_ulong_t)&adc122s_config }, - { "adc122s101", (kernel_ulong_t)&adc122s_config }, - { "adc124s021", (kernel_ulong_t)&adc124s_config }, - { "adc124s051", (kernel_ulong_t)&adc124s_config }, - { "adc124s101", (kernel_ulong_t)&adc124s_config }, - { "bd79100", (kernel_ulong_t)&bd79100_config }, - { "bd79101", (kernel_ulong_t)&bd79101_config }, - { "bd79102", (kernel_ulong_t)&bd79102_config }, - { "bd79103", (kernel_ulong_t)&bd79104_config }, - { "bd79104", (kernel_ulong_t)&bd79104_config }, + { .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); diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 08aa32bd5e4b..422df0685580 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -229,8 +229,8 @@ static const struct of_device_id ti_adc_dt_ids[] = { MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); static const struct spi_device_id ti_adc_id[] = { - { "adc141s626", TI_ADC141S626 }, - { "adc161s626", TI_ADC161S626 }, + { .name = "adc141s626", .driver_data = TI_ADC141S626 }, + { .name = "adc161s626", .driver_data = TI_ADC161S626 }, { } }; MODULE_DEVICE_TABLE(spi, ti_adc_id); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 8a272af69f7d..0fbfa4e499aa 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -930,6 +930,17 @@ static int ads1015_set_conv_mode(struct ads1015_data *data, int mode) 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; @@ -1030,6 +1041,10 @@ static int ads1015_probe(struct i2c_client *client) 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); @@ -1037,33 +1052,11 @@ static int ads1015_probe(struct i2c_client *client) return ret; pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); - pm_runtime_enable(&client->dev); - - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "Failed to register IIO device\n"); + ret = devm_pm_runtime_enable(&client->dev); + if (ret) return ret; - } - - return 0; -} -static void ads1015_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ads1015_data *data = iio_priv(indio_dev); - int ret; - - iio_device_unregister(indio_dev); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - /* power down single shot mode */ - ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); - if (ret) - dev_warn(&client->dev, "Failed to power down (%pe)\n", - ERR_PTR(ret)); + return devm_iio_device_register(&client->dev, indio_dev); } #ifdef CONFIG_PM @@ -1150,7 +1143,6 @@ static struct i2c_driver ads1015_driver = { .pm = &ads1015_pm_ops, }, .probe = ads1015_probe, - .remove = ads1015_remove, .id_table = ads1015_id, }; diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c index d6624c71a374..d9ec4a8d7ce5 100644 --- a/drivers/iio/adc/ti-ads1018.c +++ b/drivers/iio/adc/ti-ads1018.c @@ -717,8 +717,8 @@ static const struct of_device_id ads1018_of_match[] = { MODULE_DEVICE_TABLE(of, ads1018_of_match); static const struct spi_device_id ads1018_spi_match[] = { - { "ads1018", (kernel_ulong_t)&ads1018_chip_info }, - { "ads1118", (kernel_ulong_t)&ads1118_chip_info }, + { .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); diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c new file mode 100644 index 000000000000..60eab4852ba2 --- /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 <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> +#include <linux/device/devres.h> +#include <linux/i2c.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/math64.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/sysfs.h> +#include <linux/time64.h> +#include <linux/types.h> +#include <linux/unaligned.h> +#include <linux/units.h> + +/* 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) <dlechner@baylibre.com>"); +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 index 45de47778809..49fbc63508ac 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -348,8 +348,8 @@ static int ads124s_probe(struct spi_device *spi) } static const struct spi_device_id ads124s_id[] = { - { "ads124s06", ADS124S06_ID }, - { "ads124s08", ADS124S08_ID }, + { .name = "ads124s06", .driver_data = ADS124S06_ID }, + { .name = "ads124s08", .driver_data = ADS124S08_ID }, { } }; MODULE_DEVICE_TABLE(spi, ads124s_id); diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index 9e47ace16acd..06c6353aa1b2 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -743,7 +743,7 @@ static int ads1298_probe(struct spi_device *spi) } static const struct spi_device_id ads1298_id[] = { - { "ads1298" }, + { .name = "ads1298" }, { } }; MODULE_DEVICE_TABLE(spi, ads1298_id); diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index a585621b0bc3..1d25f887cadb 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -917,9 +917,9 @@ static const struct of_device_id ads131e08_of_match[] = { MODULE_DEVICE_TABLE(of, ads131e08_of_match); static const struct spi_device_id ads131e08_ids[] = { - { "ads131e04", (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] }, - { "ads131e06", (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] }, - { "ads131e08", (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] }, + { .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); diff --git a/drivers/iio/adc/ti-ads131m02.c b/drivers/iio/adc/ti-ads131m02.c index 36203ce37c89..2f8f75c8216b 100644 --- a/drivers/iio/adc/ti-ads131m02.c +++ b/drivers/iio/adc/ti-ads131m02.c @@ -943,11 +943,11 @@ static const struct of_device_id ads131m_of_match[] = { MODULE_DEVICE_TABLE(of, ads131m_of_match); static const struct spi_device_id ads131m_id[] = { - { "ads131m02", (kernel_ulong_t)&ads131m02_config }, - { "ads131m03", (kernel_ulong_t)&ads131m03_config }, - { "ads131m04", (kernel_ulong_t)&ads131m04_config }, - { "ads131m06", (kernel_ulong_t)&ads131m06_config }, - { "ads131m08", (kernel_ulong_t)&ads131m08_config }, + { .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); diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 39a074bce6d5..d27c1f2885fd 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -619,18 +619,18 @@ static int ti_ads7950_probe(struct spi_device *spi) } static const struct spi_device_id ti_ads7950_id[] = { - { "ads7950", (kernel_ulong_t)&ti_ads7950_chip_info }, - { "ads7951", (kernel_ulong_t)&ti_ads7951_chip_info }, - { "ads7952", (kernel_ulong_t)&ti_ads7952_chip_info }, - { "ads7953", (kernel_ulong_t)&ti_ads7953_chip_info }, - { "ads7954", (kernel_ulong_t)&ti_ads7954_chip_info }, - { "ads7955", (kernel_ulong_t)&ti_ads7955_chip_info }, - { "ads7956", (kernel_ulong_t)&ti_ads7956_chip_info }, - { "ads7957", (kernel_ulong_t)&ti_ads7957_chip_info }, - { "ads7958", (kernel_ulong_t)&ti_ads7958_chip_info }, - { "ads7959", (kernel_ulong_t)&ti_ads7959_chip_info }, - { "ads7960", (kernel_ulong_t)&ti_ads7960_chip_info }, - { "ads7961", (kernel_ulong_t)&ti_ads7961_chip_info }, + { .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); diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index ba5e240aa41a..8ad070b5ec4c 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -453,8 +453,8 @@ static int ads8688_probe(struct spi_device *spi) } static const struct spi_device_id ads8688_id[] = { - { "ads8684", ID_ADS8684 }, - { "ads8688", ID_ADS8688 }, + { .name = "ads8684", .driver_data = ID_ADS8684 }, + { .name = "ads8688", .driver_data = ID_ADS8688 }, { } }; MODULE_DEVICE_TABLE(spi, ads8688_id); diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c index 7e57006a8a12..d248bbea0d31 100644 --- a/drivers/iio/adc/ti-lmp92064.c +++ b/drivers/iio/adc/ti-lmp92064.c @@ -357,7 +357,7 @@ static int lmp92064_adc_probe(struct spi_device *spi) } static const struct spi_device_id lmp92064_id_table[] = { - { "lmp92064" }, + { .name = "lmp92064" }, { } }; MODULE_DEVICE_TABLE(spi, lmp92064_id_table); diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c index 94bbf5afe30e..ea776f80de72 100644 --- a/drivers/iio/adc/ti-tlc4541.c +++ b/drivers/iio/adc/ti-tlc4541.c @@ -240,8 +240,8 @@ static const struct of_device_id tlc4541_dt_ids[] = { MODULE_DEVICE_TABLE(of, tlc4541_dt_ids); static const struct spi_device_id tlc4541_id[] = { - { "tlc3541", TLC3541 }, - { "tlc4541", TLC4541 }, + { .name = "tlc3541", .driver_data = TLC3541 }, + { .name = "tlc4541", .driver_data = TLC4541 }, { } }; MODULE_DEVICE_TABLE(spi, tlc4541_id); diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index aba4b10a17ac..125f6d7e2d84 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -121,11 +121,6 @@ struct tsc2046_adc_group_layout { unsigned int skip; }; -struct tsc2046_adc_dcfg { - const struct iio_chan_spec *channels; - unsigned int num_channels; -}; - struct tsc2046_adc_ch_cfg { unsigned int settling_time_us; unsigned int oversampling_ratio; @@ -141,7 +136,6 @@ enum tsc2046_state { struct tsc2046_adc_priv { struct spi_device *spi; - const struct tsc2046_adc_dcfg *dcfg; bool internal_vref; struct iio_trigger *trig; @@ -214,11 +208,6 @@ const struct iio_chan_spec name ## _channels[] = { \ static DECLARE_TI_TSC2046_8_CHANNELS(tsc2046_adc, 12); -static const struct tsc2046_adc_dcfg tsc2046_adc_dcfg_tsc2046e = { - .channels = tsc2046_adc_channels, - .num_channels = ARRAY_SIZE(tsc2046_adc_channels), -}; - /* * Convert time to a number of samples which can be transferred within this * time. @@ -739,7 +728,6 @@ static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv) static int tsc2046_adc_probe(struct spi_device *spi) { - const struct tsc2046_adc_dcfg *dcfg; struct device *dev = &spi->dev; struct tsc2046_adc_priv *priv; struct iio_dev *indio_dev; @@ -752,10 +740,6 @@ static int tsc2046_adc_probe(struct spi_device *spi) return -EINVAL; } - dcfg = spi_get_device_match_data(spi); - if (!dcfg) - return -EINVAL; - spi->mode &= ~SPI_MODE_X_MASK; spi->mode |= SPI_MODE_0; ret = spi_setup(spi); @@ -767,14 +751,13 @@ static int tsc2046_adc_probe(struct spi_device *spi) return -ENOMEM; priv = iio_priv(indio_dev); - priv->dcfg = dcfg; priv->spi = spi; indio_dev->name = TI_TSC2046_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = dcfg->channels; - indio_dev->num_channels = dcfg->num_channels; + 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"); @@ -829,13 +812,13 @@ static int tsc2046_adc_probe(struct spi_device *spi) } static const struct of_device_id ads7950_of_table[] = { - { .compatible = "ti,tsc2046e-adc", .data = &tsc2046_adc_dcfg_tsc2046e }, + { .compatible = "ti,tsc2046e-adc" }, { } }; MODULE_DEVICE_TABLE(of, ads7950_of_table); static const struct spi_device_id tsc2046_adc_spi_ids[] = { - { "tsc2046e-adc", (unsigned long)&tsc2046_adc_dcfg_tsc2046e }, + { .name = "tsc2046e-adc" }, { } }; MODULE_DEVICE_TABLE(spi, tsc2046_adc_spi_ids); diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c new file mode 100644 index 000000000000..1b55d343982e --- /dev/null +++ b/drivers/iio/adc/versal-sysmon-core.c @@ -0,0 +1,1052 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon core driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/limits.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/units.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> + +#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 <salih.erim@amd.com>"); diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c new file mode 100644 index 000000000000..e9a7629159ab --- /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 <linux/bits.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/types.h> +#include <linux/unaligned.h> + +#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 <conall.ogriofa@amd.com>"); +MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>"); diff --git a/drivers/iio/adc/versal-sysmon.c b/drivers/iio/adc/versal-sysmon.c new file mode 100644 index 000000000000..529d0486c9f9 --- /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 <linux/err.h> +#include <linux/io.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#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 <salih.erim@amd.com>"); diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h new file mode 100644 index 000000000000..bb9a75bf71c0 --- /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 <linux/bits.h> +#include <linux/mutex.h> +#include <linux/spinlock_types.h> +#include <linux/workqueue.h> + +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/addac/ad74115.c b/drivers/iio/addac/ad74115.c index 41e0b1d334cc..9b9d7a12ce87 100644 --- a/drivers/iio/addac/ad74115.c +++ b/drivers/iio/addac/ad74115.c @@ -1904,7 +1904,7 @@ static int __init ad74115_register_driver(struct spi_driver *spi) } static const struct spi_device_id ad74115_spi_id[] = { - { "ad74115h" }, + { .name = "ad74115h" }, { } }; diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 43bd2079cf6d..2e5a58e48a89 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -1457,7 +1457,7 @@ static int ad74413r_probe(struct spi_device *spi) ret = devm_request_irq(st->dev, spi->irq, ad74413r_adc_data_interrupt, 0, st->chip_info->name, indio_dev); if (ret) - return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + return ret; ret = devm_iio_triggered_buffer_setup(st->dev, indio_dev, &iio_pollfunc_store_time, diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index 654a3a50eb4f..3a6a09ffa526 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -608,3 +608,4 @@ module_platform_driver(rescale_driver); MODULE_DESCRIPTION("IIO rescale driver"); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index affc9c9d8488..e12f8604f59f 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -351,19 +351,19 @@ static int ad8366_probe(struct spi_device *spi) } static const struct spi_device_id ad8366_id[] = { - { "ad8366", (kernel_ulong_t)&ad8366_chip_info }, - { "ada4961", (kernel_ulong_t)&ada4961_chip_info }, - { "adl5240", (kernel_ulong_t)&adl5240_chip_info }, - { "adrf5702", (kernel_ulong_t)&adrf5702_chip_info }, - { "adrf5703", (kernel_ulong_t)&adrf5703_chip_info }, - { "adrf5720", (kernel_ulong_t)&adrf5720_chip_info }, - { "adrf5730", (kernel_ulong_t)&adrf5730_chip_info }, - { "adrf5731", (kernel_ulong_t)&adrf5731_chip_info }, - { "hmc271a", (kernel_ulong_t)&hmc271_chip_info }, - { "hmc792a", (kernel_ulong_t)&hmc792_chip_info }, - { "hmc1018a", (kernel_ulong_t)&hmc1018_chip_info }, - { "hmc1019a", (kernel_ulong_t)&hmc1019_chip_info }, - { "hmc1119", (kernel_ulong_t)&hmc1119_chip_info }, + { .name = "ad8366", .driver_data = (kernel_ulong_t)&ad8366_chip_info }, + { .name = "ada4961", .driver_data = (kernel_ulong_t)&ada4961_chip_info }, + { .name = "adl5240", .driver_data = (kernel_ulong_t)&adl5240_chip_info }, + { .name = "adrf5702", .driver_data = (kernel_ulong_t)&adrf5702_chip_info }, + { .name = "adrf5703", .driver_data = (kernel_ulong_t)&adrf5703_chip_info }, + { .name = "adrf5720", .driver_data = (kernel_ulong_t)&adrf5720_chip_info }, + { .name = "adrf5730", .driver_data = (kernel_ulong_t)&adrf5730_chip_info }, + { .name = "adrf5731", .driver_data = (kernel_ulong_t)&adrf5731_chip_info }, + { .name = "hmc271a", .driver_data = (kernel_ulong_t)&hmc271_chip_info }, + { .name = "hmc792a", .driver_data = (kernel_ulong_t)&hmc792_chip_info }, + { .name = "hmc1018a", .driver_data = (kernel_ulong_t)&hmc1018_chip_info }, + { .name = "hmc1019a", .driver_data = (kernel_ulong_t)&hmc1019_chip_info }, + { .name = "hmc1119", .driver_data = (kernel_ulong_t)&hmc1119_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad8366_id); diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c index 71e361af2074..9702de3a5074 100644 --- a/drivers/iio/amplifiers/ada4250.c +++ b/drivers/iio/amplifiers/ada4250.c @@ -358,7 +358,7 @@ static int ada4250_probe(struct spi_device *spi) } static const struct spi_device_id ada4250_id[] = { - { "ada4250", 0 }, + { .name = "ada4250" }, { } }; MODULE_DEVICE_TABLE(spi, ada4250_id); diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c index 0c266c216525..b58263bcf3cb 100644 --- a/drivers/iio/buffer/industrialio-buffer-cb.c +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -154,3 +154,4 @@ EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev); MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); MODULE_DESCRIPTION("Industrial I/O callback buffer"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 98acce909854..ecc02a427b92 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -56,6 +56,23 @@ static void iio_dmaengine_buffer_block_done(void *data, iio_dma_buffer_block_done(block); } +/* + * Cyclic transfers run until abort. Cap active cyclic blocks to one until there + * is a use case for more. + */ +static bool iio_dmaengine_buffer_has_active_cyclic(struct dmaengine_buffer *dmaengine_buffer) +{ + struct iio_dma_buffer_block *block; + + guard(spinlock_irqsave)(&dmaengine_buffer->queue.list_lock); + list_for_each_entry(block, &dmaengine_buffer->active, head) { + if (block->cyclic) + return true; + } + + return false; +} + static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, struct iio_dma_buffer_block *block) { @@ -79,7 +96,14 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, else dma_dir = DMA_MEM_TO_DEV; + if (block->cyclic && iio_dmaengine_buffer_has_active_cyclic(dmaengine_buffer)) { + dev_err(queue->dev, "cyclic DMA transfer already active\n"); + return -EBUSY; + } + if (block->sg_table) { + unsigned long flags; + sgl = block->sg_table->sgl; nents = sg_nents_for_len(sgl, block->bytes_used); if (nents < 0) @@ -99,9 +123,19 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, sgl = sg_next(sgl); } + if (block->cyclic) + flags = DMA_PREP_REPEAT; + else + flags = DMA_PREP_INTERRUPT; + + /* + * A new transfer may need to end an already active cyclic transfer + * before it can run, so always set the EOT flag. + */ + flags |= DMA_PREP_LOAD_EOT; desc = dmaengine_prep_peripheral_dma_vec(dmaengine_buffer->chan, vecs, nents, dma_dir, - DMA_PREP_INTERRUPT); + flags); kfree(vecs); } else { max_size = min(block->size, dmaengine_buffer->max_size); @@ -122,8 +156,10 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, if (!desc) return -ENOMEM; - desc->callback_result = iio_dmaengine_buffer_block_done; - desc->callback_param = block; + if (!block->cyclic) { + desc->callback_result = iio_dmaengine_buffer_block_done; + desc->callback_param = block; + } cookie = dmaengine_submit(desc); if (dma_submit_error(cookie)) diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 10e912bbf0c5..d035aa33b357 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -216,3 +216,4 @@ EXPORT_SYMBOL_GPL(iio_hw_consumer_disable); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Hardware consumer buffer the IIO framework"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 4cc0e491621a..c34d67bcb2a3 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -24,10 +24,9 @@ static int bme680_i2c_probe(struct i2c_client *client) const char *name = NULL; regmap = devm_regmap_init_i2c(client, &bme680_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "Failed to register i2c regmap\n"); if (id) name = id->name; diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index 785200a6fd65..2175b9564fa1 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -121,16 +121,15 @@ static int bme680_spi_probe(struct spi_device *spi) regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus, bus_context, &bme680_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(regmap), + "Failed to register spi regmap\n"); return bme680_core_probe(&spi->dev, regmap, id->name); } static const struct spi_device_id bme680_spi_id[] = { - {"bme680", 0}, + { .name = "bme680" }, { } }; MODULE_DEVICE_TABLE(spi, bme680_spi_id); diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index ce7187ccd706..d126ee12ffe5 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -484,10 +484,8 @@ static int ccs811_probe(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "ccs811_irq", indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) goto err_poweroff; - } data->drdy_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c index bbc96c4c6283..7c1ffda38b99 100644 --- a/drivers/iio/chemical/ens160_core.c +++ b/drivers/iio/chemical/ens160_core.c @@ -320,7 +320,7 @@ static int ens160_setup_trigger(struct iio_dev *indio_dev, int irq) IRQF_NO_THREAD, indio_dev->name, indio_dev->trig); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; return 0; } diff --git a/drivers/iio/chemical/ens160_spi.c b/drivers/iio/chemical/ens160_spi.c index a674c0e1bf4b..7c11d19291c9 100644 --- a/drivers/iio/chemical/ens160_spi.c +++ b/drivers/iio/chemical/ens160_spi.c @@ -39,7 +39,7 @@ static const struct of_device_id ens160_spi_of_match[] = { MODULE_DEVICE_TABLE(of, ens160_spi_of_match); static const struct spi_device_id ens160_spi_id[] = { - { "ens160" }, + { .name = "ens160" }, { } }; MODULE_DEVICE_TABLE(spi, ens160_spi_id); diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index c5425136814b..770571c21521 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor core driver * - * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com> */ #include <linux/bitfield.h> @@ -686,7 +686,7 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) IRQF_NO_AUTOEN, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; return 0; } @@ -711,7 +711,11 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, state->pressure_comp = SCD30_PRESSURE_COMP_DEFAULT; state->meas_interval = SCD30_MEAS_INTERVAL_DEFAULT; state->command = command; - mutex_init(&state->lock); + + ret = devm_mutex_init(dev, &state->lock); + if (ret) + return ret; + init_completion(&state->meas_ready); dev_set_drvdata(dev, indio_dev); @@ -770,6 +774,6 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, } EXPORT_SYMBOL_NS(scd30_probe, "IIO_SCD30"); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/scd30_i2c.c b/drivers/iio/chemical/scd30_i2c.c index abceccdddc71..bcef1e473e34 100644 --- a/drivers/iio/chemical/scd30_i2c.c +++ b/drivers/iio/chemical/scd30_i2c.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor i2c driver * - * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com> * * I2C slave address: 0x61 */ @@ -136,7 +136,7 @@ static struct i2c_driver scd30_i2c_driver = { }; module_i2c_driver(scd30_i2c_driver); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SCD30"); diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c index 7fefdadbfa0a..31b0fc957b08 100644 --- a/drivers/iio/chemical/scd30_serial.c +++ b/drivers/iio/chemical/scd30_serial.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor serial driver * - * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com> */ #include <linux/crc16.h> #include <linux/device.h> @@ -257,7 +257,7 @@ static struct serdev_device_driver scd30_serdev_driver = { }; module_serdev_device_driver(scd30_serdev_driver); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor serial driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SCD30"); diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index f10bbebc29e4..379c1c4af8d8 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -548,6 +548,9 @@ static int sgp_probe(struct i2c_client *client) data->iaq_thread = kthread_run(sgp_iaq_threadfn, data, "%s-iaq", data->client->name); + if (IS_ERR(data->iaq_thread)) + return dev_err_probe(dev, PTR_ERR(data->iaq_thread), + "failed to start IAQ thread\n"); return 0; } diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c index 90f1adb8c89f..2d3a876cc06e 100644 --- a/drivers/iio/chemical/sps30_i2c.c +++ b/drivers/iio/chemical/sps30_i2c.c @@ -2,7 +2,7 @@ /* * Sensirion SPS30 particulate matter sensor i2c driver * - * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com> * * I2C slave address: 0x69 */ @@ -252,7 +252,7 @@ static struct i2c_driver sps30_i2c_driver = { }; module_i2c_driver(sps30_i2c_driver); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SPS30"); diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c index 80eab9b2e4bf..0cdba8703e93 100644 --- a/drivers/iio/chemical/sps30_serial.c +++ b/drivers/iio/chemical/sps30_serial.c @@ -2,7 +2,7 @@ /* * Sensirion SPS30 particulate matter sensor serial driver * - * Copyright (c) 2021 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2021 Tomasz Duszynski <tduszyns@gmail.com> */ #include <linux/completion.h> #include <linux/device.h> @@ -425,7 +425,7 @@ static struct serdev_device_driver sps30_serial_driver = { }; module_serdev_device_driver(sps30_serial_driver); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor serial driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SPS30"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index c115a72832b2..9e7c26e37880 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -72,8 +72,7 @@ static const struct { {HID_USAGE_SENSOR_HUMAN_ATTENTION, 0, 1, 0}, }; -static void simple_div(int dividend, int divisor, int *whole, - int *micro_frac) +static void simple_div(int dividend, int divisor, int *whole, int *micro_frac) { int rem; int exp = 0; @@ -111,7 +110,7 @@ for 10^-2. Negative numbers are 2's complement */ static void convert_from_vtf_format(u32 value, int size, int exp, - int *val1, int *val2) + int *val1, int *val2) { int sign = 1; @@ -176,7 +175,7 @@ s32 hid_sensor_read_poll_value(struct hid_sensor_common *st) EXPORT_SYMBOL_NS(hid_sensor_read_poll_value, "IIO_HID_ATTRIBUTES"); int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, - int *val1, int *val2) + int *val1, int *val2) { s32 value; int ret; @@ -203,7 +202,7 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_read_samp_freq_value, "IIO_HID"); int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, - int val1, int val2) + int val1, int val2) { s32 value; int ret; @@ -238,15 +237,15 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_write_samp_freq_value, "IIO_HID"); int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, - int *val1, int *val2) + int *val1, int *val2) { s32 value; int ret; ret = sensor_hub_get_feature(st->hsdev, st->sensitivity.report_id, - st->sensitivity.index, sizeof(value), - &value); + st->sensitivity.index, + sizeof(value), &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -268,8 +267,8 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity_rel.report_id, - st->sensitivity_rel.index, sizeof(value), - &value); + st->sensitivity_rel.index, + sizeof(value), &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -282,9 +281,8 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, } EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, "IIO_HID"); - int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, - int val1, int val2) + int val1, int val2) { s32 value; int ret; @@ -293,8 +291,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, return -EINVAL; value = convert_to_vtf_format(st->sensitivity.size, - st->sensitivity.unit_expo, - val1, val2); + st->sensitivity.unit_expo, + val1, val2); ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id, st->sensitivity.index, sizeof(value), &value); @@ -303,8 +301,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity.report_id, - st->sensitivity.index, sizeof(value), - &value); + st->sensitivity.index, + sizeof(value), &value); if (ret < 0 || value < 0) return -EINVAL; @@ -324,8 +322,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, return -EINVAL; value = convert_to_vtf_format(st->sensitivity_rel.size, - st->sensitivity_rel.unit_expo, - val1, val2); + st->sensitivity_rel.unit_expo, + val1, val2); ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id, st->sensitivity_rel.index, sizeof(value), &value); @@ -334,8 +332,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity_rel.report_id, - st->sensitivity_rel.index, sizeof(value), - &value); + st->sensitivity_rel.index, + sizeof(value), &value); if (ret < 0 || value < 0) return -EINVAL; @@ -356,8 +354,8 @@ EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, "IIO_HID"); * 1.001745329 ->exp:4-> val0[10017]val1[453290000] * 9.806650000 ->exp:-2-> val0[0]val1[98066500] */ -static void adjust_exponent_nano(int *val0, int *val1, int scale0, - int scale1, int exp) +static void adjust_exponent_nano(int *val0, int *val1, + int scale0, int scale1, int exp) { int divisor; int i; @@ -404,8 +402,8 @@ static void adjust_exponent_nano(int *val0, int *val1, int scale0, } int hid_sensor_format_scale(u32 usage_id, - struct hid_sensor_hub_attribute_info *attr_info, - int *val0, int *val1) + struct hid_sensor_hub_attribute_info *attr_info, + int *val0, int *val1) { int i; int exp; @@ -415,12 +413,11 @@ int hid_sensor_format_scale(u32 usage_id, for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) { if (unit_conversion[i].usage_id == usage_id && - unit_conversion[i].unit == attr_info->units) { - exp = hid_sensor_convert_exponent( - attr_info->unit_expo); + unit_conversion[i].unit == attr_info->units) { + exp = hid_sensor_convert_exponent(attr_info->unit_expo); adjust_exponent_nano(val0, val1, - unit_conversion[i].scale_val0, - unit_conversion[i].scale_val1, exp); + unit_conversion[i].scale_val0, + unit_conversion[i].scale_val1, exp); break; } } @@ -438,8 +435,8 @@ EXPORT_SYMBOL_NS(hid_sensor_convert_timestamp, "IIO_HID"); static int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct hid_sensor_common *st) + u32 usage_id, + struct hid_sensor_common *st) { sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -473,9 +470,10 @@ int hid_sensor_get_report_latency(struct hid_sensor_common *st) int ret; int value; - ret = sensor_hub_get_feature(st->hsdev, st->report_latency.report_id, - st->report_latency.index, sizeof(value), - &value); + ret = sensor_hub_get_feature(st->hsdev, + st->report_latency.report_id, + st->report_latency.index, + sizeof(value), &value); if (ret < 0) return ret; @@ -498,10 +496,10 @@ bool hid_sensor_batch_mode_supported(struct hid_sensor_common *st) EXPORT_SYMBOL_NS(hid_sensor_batch_mode_supported, "IIO_HID_ATTRIBUTES"); int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct hid_sensor_common *st, - const u32 *sensitivity_addresses, - u32 sensitivity_addresses_len) + u32 usage_id, + struct hid_sensor_common *st, + const u32 *sensitivity_addresses, + u32 sensitivity_addresses_len) { struct hid_sensor_hub_attribute_info timestamp; @@ -527,7 +525,7 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, - &st->sensitivity); + &st->sensitivity); sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -578,8 +576,9 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, timestamp.index, timestamp.report_id); ret = sensor_hub_get_feature(hsdev, - st->power_state.report_id, - st->power_state.index, sizeof(value), &value); + st->power_state.report_id, + st->power_state.index, + sizeof(value), &value); if (ret < 0) return ret; if (value < 0) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 417c4ab8c1b2..60808e2430ca 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -233,7 +233,7 @@ void hid_sensor_remove_trigger(struct iio_dev *indio_dev, EXPORT_SYMBOL_NS(hid_sensor_remove_trigger, "IIO_HID"); int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, - struct hid_sensor_common *attrb) + struct hid_sensor_common *attrb) { const struct iio_dev_attr **fifo_attrs; int ret; @@ -266,7 +266,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, trig = iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", name, iio_device_id(indio_dev)); - if (trig == NULL) { + if (!trig) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); return -ENOMEM; } @@ -314,7 +314,9 @@ static int __maybe_unused hid_sensor_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + schedule_work(&attrb->work); + return 0; } @@ -322,6 +324,7 @@ static int __maybe_unused hid_sensor_runtime_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + return _hid_sensor_power_state(attrb, true); } diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h index f94fca4f1edf..589de858e369 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h @@ -15,7 +15,7 @@ struct iio_dev; extern const struct dev_pm_ops hid_sensor_pm_ops; int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, - struct hid_sensor_common *attrb); + struct hid_sensor_common *attrb); void hid_sensor_remove_trigger(struct iio_dev *indio_dev, struct hid_sensor_common *attrb); int hid_sensor_power_state(struct hid_sensor_common *st, bool state); diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index e0b10366ed2b..88a82d1370c5 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -10,18 +10,19 @@ #include <linux/iio/common/inv_sensors_timestamp.h> -/* compute jitter, min and max following jitter in per mille */ -#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter) \ - (div_s64((_val) * (_jitter), 1000)) +/* compute min and max following jitter in per mille */ #define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter) \ (((_val) * (1000 - (_jitter))) / 1000) #define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter) \ (((_val) * (1000 + (_jitter))) / 1000) +/* minimum timestamp delta between 2 interrupts for measuring period (20ms) */ +#define INV_SENSORS_MIN_IT_DELTA (20 * NSEC_PER_MSEC) + /* Add a new value inside an accumulator and update the estimate value */ -static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val) +static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, u32 val) { - uint64_t sum = 0; + u64 sum = 0; size_t i; acc->values[acc->idx++] = val; @@ -58,9 +59,9 @@ void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts, EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, "IIO_INV_SENSORS_TIMESTAMP"); int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, - uint32_t period, bool fifo) + u32 period, bool fifo) { - uint32_t mult; + u32 mult; /* when FIFO is on, prevent odr change if one is already pending */ if (fifo && ts->new_mult != 0) @@ -78,9 +79,9 @@ int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, } EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, "IIO_INV_SENSORS_TIMESTAMP"); -static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period) +static bool inv_validate_period(struct inv_sensors_timestamp *ts, u32 period) { - uint32_t period_min, period_max; + u32 period_min, period_max; /* check that period is acceptable */ period_min = ts->min_period * ts->mult; @@ -92,9 +93,9 @@ static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t perio } static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, - uint32_t period) + u32 period) { - uint32_t new_chip_period; + u32 new_chip_period; if (!inv_validate_period(ts, period)) return false; @@ -102,78 +103,84 @@ static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, /* update chip internal period estimation */ new_chip_period = period / ts->mult; inv_update_acc(&ts->chip_period, new_chip_period); - ts->period = ts->mult * ts->chip_period.val; return true; } -static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts) +static u32 inv_align_timestamp_it(struct inv_sensors_timestamp *ts, + unsigned int sample_nb) { - const int64_t period_min = (int64_t)ts->min_period * ts->mult; - const int64_t period_max = (int64_t)ts->max_period * ts->mult; - int64_t add_max, sub_max; - int64_t delta, jitter; - int64_t adjust; - - /* delta time between last sample and last interrupt */ - delta = ts->it.lo - ts->timestamp; - - /* adjust timestamp while respecting jitter */ - add_max = period_max - (int64_t)ts->period; - sub_max = period_min - (int64_t)ts->period; - jitter = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter); - if (delta > jitter) - adjust = add_max; - else if (delta < -jitter) - adjust = sub_max; - else - adjust = 0; + const s64 period_min = (s64)ts->min_period * ts->mult; + const s64 period_max = (s64)ts->max_period * ts->mult; + s64 new_period; - ts->timestamp += adjust; + /* compute new period aligning last timestamp with interrupt timestamp */ + new_period = div_s64(ts->it.up - ts->timestamp, sample_nb); + + /* ensure that period never overflows the jitter */ + return clamp(new_period, period_min, period_max); } void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, - size_t sample_nb, int64_t timestamp) + size_t sample_nb, s64 timestamp) { struct inv_sensors_timestamp_interval *it; - int64_t delta, interval; - uint32_t period; + s64 delta, delta_threshold, interval; + u32 period; bool valid = false; if (sample_nb == 0) return; + /* no previous data, compute theoretical value from interrupt */ + if (ts->timestamp == 0) { + /* elapsed time: sensor period * sensor samples number */ + interval = (s64)ts->period * (s64)sample_nb; + ts->timestamp = timestamp - interval; + } + + /* update delta timestamps and estimated period */ + it = &ts->delta; + ts->delta_counter += sample_nb; + delta = timestamp - it->up; + delta_threshold = INV_SENSORS_TIMESTAMP_MIN(INV_SENSORS_MIN_IT_DELTA, ts->chip.jitter); + if (delta >= delta_threshold) { + it->lo = it->up; + it->up = timestamp; + if (it->lo != 0) { + /* compute period: delta time divided by number of samples */ + delta = it->up - it->lo; + period = div_s64(delta, ts->delta_counter); + inv_update_chip_period(ts, period); + } + ts->delta_counter = 0; + } + /* update interrupt timestamp and compute chip and sensor periods */ it = &ts->it; it->lo = it->up; it->up = timestamp; delta = it->up - it->lo; if (it->lo != 0) { - /* compute period: delta time divided by number of samples */ + /* compute period and check validity */ period = div_s64(delta, sample_nb); - valid = inv_update_chip_period(ts, period); - } - - /* no previous data, compute theoretical value from interrupt */ - if (ts->timestamp == 0) { - /* elapsed time: sensor period * sensor samples number */ - interval = (int64_t)ts->period * (int64_t)sample_nb; - ts->timestamp = it->up - interval; - return; + valid = inv_validate_period(ts, period); } /* if interrupt interval is valid, sync with interrupt timestamp */ if (valid) - inv_align_timestamp_it(ts); + ts->period = inv_align_timestamp_it(ts, sample_nb); + else + ts->period = ts->mult * ts->chip_period.val; } EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, "IIO_INV_SENSORS_TIMESTAMP"); void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, - uint32_t fifo_period, size_t fifo_nb, + u32 fifo_period, size_t fifo_nb, unsigned int fifo_no) { - int64_t interval; - uint32_t fifo_mult; + s64 interval; + u32 fifo_mult; if (ts->new_mult == 0) return; @@ -194,7 +201,7 @@ void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, fifo_mult = fifo_period / ts->chip.clock_period; fifo_period = fifo_mult * ts->chip_period.val; /* computes time interval between interrupt and this sample */ - interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period; + interval = (s64)(fifo_nb - fifo_no) * (s64)fifo_period; ts->timestamp = ts->it.up - interval; } } diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 8a8ab688d798..7b3e9732cc3e 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -211,10 +211,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, irq_trig, sdata->trig->name, sdata->trig); - if (err) { - dev_err(parent, "failed to request trigger IRQ.\n"); + if (err) return err; - } err = devm_iio_trigger_register(parent, sdata->trig); if (err < 0) { diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 14a246729d2b..3b3c77d8b3e1 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -11,8 +11,11 @@ config AD3530R depends on SPI select REGMAP_SPI help - Say yes here to build support for Analog Devices AD3530R, AD3531R - Digital to Analog Converter. + Say yes here to build support for the following Analog Devices + Digital to Analog Converters: + - AD3530/AD3530R (8-channel) + - AD3531/AD3531R (4-channel) + - AD3532/AD3532R (16-channel) To compile this driver as a module, choose M here: the module will be called ad3530r. @@ -240,15 +243,26 @@ config LTC2688 config AD5686 tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER config AD5686_SPI tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)" depends on SPI select AD5686 help - Say yes here to build support for Analog Devices AD5672R, AD5674R, - AD5676, AD5676R, AD5679R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, - AD5686R Voltage Output Digital to Analog Converter. + Say yes here to build support for Analog Devices Voltage Output + Digital to Analog Converters: + - Single-channel: + AD5310R, AD5681R, AD5682R, AD5683, AD5683R + - Dual-channel: + AD5313R, AD5687, AD5687R, AD5689, AD5689R + - Quad-channel: + AD5317R, AD5684, AD5684R, AD5685R, AD5686, AD5686R + - 8-channel: + AD5672R, AD5676, AD5676R + - 16-channel: + AD5674, AD5674R, AD5679, AD5679R To compile this driver as a module, choose M here: the module will be called ad5686. @@ -258,10 +272,18 @@ config AD5696_I2C depends on I2C select AD5686 help - Say yes here to build support for Analog Devices AD5311R, AD5337, - AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, - AD5693R, AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to - Analog converters. + Say yes here to build support for Analog Devices Voltage Output + Digital to Analog Converters: + - Single-channel: + AD5311R, AD5691R, AD5692R, AD5693, AD5693R + - Dual-channel: + AD5338R, AD5697R + - Quad-channel: + AD5316R, AD5694, AD5694R, AD5695R, AD5696, AD5696R + - 8-channel: + AD5671R, AD5675, AD5675R + - 16-channel: + AD5673R, AD5677R To compile this driver as a module, choose M here: the module will be called ad5696. @@ -527,6 +549,17 @@ config MAX5821 Say yes here to build support for Maxim MAX5821 10 bits DAC. +config MCF54415_DAC + tristate "NXP MCF54415 DAC driver" + depends on M5441x || COMPILE_TEST + select REGMAP_MMIO + help + Say yes here if you want to build support for NXP ColdFire + MCF54415/6/7/8 12-bit DAC module. + + To compile this driver as a module, choose M here: the module + will be called mcf54415_dac. + config MCP4725 tristate "MCP4725/6 DAC driver" depends on I2C @@ -549,6 +582,16 @@ config MCP4728 To compile this driver as a module, choose M here: the module will be called mcp4728. +config MCP47A1 + tristate "MCP47A1 DAC driver" + depends on I2C + help + Say Y here if you want to build a driver for the Microchip + MCP47A1 digital-to-analog converter with an I2C interface. + + To compile this driver as a module, choose M here: the module + will be called mcp47a1. + config MCP47FEB02 tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver" depends on I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 003431798498..992f8930f95c 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -52,8 +52,10 @@ obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MAX22007) += max22007.o obj-$(CONFIG_MAX5522) += max5522.o obj-$(CONFIG_MAX5821) += max5821.o +obj-$(CONFIG_MCF54415_DAC) += mcf54415_dac.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4728) += mcp4728.o +obj-$(CONFIG_MCP47A1) += mcp47a1.o obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o obj-$(CONFIG_MCP4821) += mcp4821.o obj-$(CONFIG_MCP4922) += mcp4922.o diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index 4e911bfb6fc5..06b7f280f61b 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -2,6 +2,7 @@ /* * AD3530R/AD3530 8-channel, 16-bit Voltage Output DAC Driver * AD3531R/AD3531 4-channel, 16-bit Voltage Output DAC Driver + * AD3532R/AD3532 16-channel, 16-bit Voltage Output DAC Driver * * Copyright 2025 Analog Devices Inc. */ @@ -38,6 +39,25 @@ #define AD3531R_SW_LDAC_TRIG_A 0xDD #define AD3531R_INPUT_CH 0xE3 +/* AD3532R/AD3532 bank 0 registers (channels 0-7) */ +#define AD3532R_INTERFACE_CONFIG_A_0 0x1000 +#define AD3532R_OUTPUT_OPERATING_MODE_0 0x1020 +#define AD3532R_OUTPUT_OPERATING_MODE_1 0x1021 +#define AD3532R_OUTPUT_CONTROL_0 0x102A +#define AD3532R_REFERENCE_CONTROL_0 0x103C +#define AD3532R_SW_LDAC_TRIG_0 0x10E5 +#define AD3532R_INPUT_CH_0 0x10EB + +/* AD3532R/AD3532 bank 1 registers (channels 8-15) */ +#define AD3532R_INTERFACE_CONFIG_A_1 0x3000 +#define AD3532R_OUTPUT_OPERATING_MODE_2 0x3020 +#define AD3532R_OUTPUT_OPERATING_MODE_3 0x3021 +#define AD3532R_OUTPUT_CONTROL_1 0x302A +#define AD3532R_REFERENCE_CONTROL_1 0x303C +#define AD3532R_SW_LDAC_TRIG_1 0x30E5 +#define AD3532R_INPUT_CH_1 0x30EB +#define AD3532R_MAX_REG_ADDR 0x30F9 + #define AD3530R_SLD_TRIG_A BIT(7) #define AD3530R_OUTPUT_CONTROL_RANGE BIT(2) #define AD3530R_REFERENCE_CONTROL_SEL BIT(0) @@ -49,8 +69,10 @@ #define AD3530R_LDAC_PULSE_US 100 #define AD3530R_DAC_MAX_VAL GENMASK(15, 0) -#define AD3530R_MAX_CHANNELS 8 +#define AD3530R_CH_PER_REG 4 +#define AD3530R_CH_PER_BANK 8 #define AD3531R_MAX_CHANNELS 4 +#define AD3532R_MAX_CHANNELS 16 enum ad3530r_mode { AD3530R_NORMAL_OP, @@ -67,9 +89,16 @@ struct ad3530r_chan { struct ad3530r_chip_info { const char *name; const struct iio_chan_spec *channels; + const struct regmap_config *regmap_config; int (*input_ch_reg)(unsigned int channel); + int (*sw_ldac_trig_reg)(unsigned int channel); + const unsigned int *interface_config_a; + const unsigned int *output_control; + const unsigned int *reference_control; + const unsigned int *op_mode; unsigned int num_channels; - unsigned int sw_ldac_trig_reg; + unsigned int num_banks; + unsigned int num_op_mode_regs; bool internal_ref_support; }; @@ -77,7 +106,7 @@ struct ad3530r_state { struct regmap *regmap; /* lock to protect against multiple access to the device and shared data */ struct mutex lock; - struct ad3530r_chan chan[AD3530R_MAX_CHANNELS]; + struct ad3530r_chan chan[AD3532R_MAX_CHANNELS]; const struct ad3530r_chip_info *chip_info; struct gpio_desc *ldac_gpio; int vref_mV; @@ -98,6 +127,14 @@ static int ad3531r_input_ch_reg(unsigned int channel) return 2 * channel + AD3531R_INPUT_CH; } +static int ad3532r_input_ch_reg(unsigned int channel) +{ + unsigned int bank = channel / AD3530R_CH_PER_BANK; + unsigned int local_ch = channel % AD3530R_CH_PER_BANK; + + return 2 * local_ch + (bank ? AD3532R_INPUT_CH_1 : AD3532R_INPUT_CH_0); +} + static const char * const ad3530r_powerdown_modes[] = { "1kohm_to_gnd", "7.7kohm_to_gnd", @@ -110,6 +147,12 @@ static const char * const ad3531r_powerdown_modes[] = { "16kohm_to_gnd", }; +static const char * const ad3532r_powerdown_modes[] = { + "1kohm_to_gnd", + "10kohm_to_gnd", + "three_state", +}; + static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -145,6 +188,13 @@ static const struct iio_enum ad3531r_powerdown_mode_enum = { .set = ad3530r_set_powerdown_mode, }; +static const struct iio_enum ad3532r_powerdown_mode_enum = { + .items = ad3532r_powerdown_modes, + .num_items = ARRAY_SIZE(ad3532r_powerdown_modes), + .get = ad3530r_get_powerdown_mode, + .set = ad3530r_set_powerdown_mode, +}; + static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, @@ -189,6 +239,62 @@ static ssize_t ad3530r_set_dac_powerdown(struct iio_dev *indio_dev, return len; } +static ssize_t ad3532r_set_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + unsigned int bank, local_ch, reg_in_bank, ch_in_reg; + unsigned int reg, mask, val; + bool powerdown; + int ret; + + ret = kstrtobool(buf, &powerdown); + if (ret) + return ret; + + bank = chan->channel / AD3530R_CH_PER_BANK; + local_ch = chan->channel % AD3530R_CH_PER_BANK; + reg_in_bank = local_ch / AD3530R_CH_PER_REG; + ch_in_reg = local_ch % AD3530R_CH_PER_REG; + + reg = reg_in_bank + (bank ? AD3532R_OUTPUT_OPERATING_MODE_2 : + AD3532R_OUTPUT_OPERATING_MODE_0); + mask = AD3530R_OP_MODE_CHAN_MSK(ch_in_reg); + + guard(mutex)(&st->lock); + if (powerdown) { + val = field_prep(mask, st->chan[chan->channel].powerdown_mode); + ret = regmap_update_bits(st->regmap, reg, mask, val); + } else { + ret = regmap_clear_bits(st->regmap, reg, mask); + } + if (ret) + return ret; + + st->chan[chan->channel].powerdown = powerdown; + + return len; +} + +static int ad3530r_trigger_sw_ldac_reg(unsigned int channel) +{ + return AD3530R_SW_LDAC_TRIG_A; +} + +static int ad3531r_trigger_sw_ldac_reg(unsigned int channel) +{ + return AD3531R_SW_LDAC_TRIG_A; +} + +static int ad3532r_trigger_sw_ldac_reg(unsigned int channel) +{ + unsigned int bank = channel / AD3530R_CH_PER_BANK; + + return bank ? AD3532R_SW_LDAC_TRIG_1 : AD3532R_SW_LDAC_TRIG_0; +} + static int ad3530r_trigger_hw_ldac(struct gpio_desc *ldac_gpio) { gpiod_set_value_cansleep(ldac_gpio, 1); @@ -214,7 +320,7 @@ static int ad3530r_dac_write(struct ad3530r_state *st, unsigned int chan, if (st->ldac_gpio) return ad3530r_trigger_hw_ldac(st->ldac_gpio); - return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg, + return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg(chan), AD3530R_SLD_TRIG_A); } @@ -301,6 +407,19 @@ static const struct iio_chan_spec_ext_info ad3531r_ext_info[] = { { } }; +static const struct iio_chan_spec_ext_info ad3532r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad3530r_get_dac_powerdown, + .write = ad3532r_set_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3532r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &ad3532r_powerdown_mode_enum), + { } +}; + #define AD3530R_CHAN(_chan, _ext_info) \ { \ .type = IIO_VOLTAGE, \ @@ -330,48 +449,215 @@ static const struct iio_chan_spec ad3531r_channels[] = { AD3530R_CHAN(3, ad3531r_ext_info), }; +static const struct iio_chan_spec ad3532r_channels[] = { + AD3530R_CHAN(0, ad3532r_ext_info), + AD3530R_CHAN(1, ad3532r_ext_info), + AD3530R_CHAN(2, ad3532r_ext_info), + AD3530R_CHAN(3, ad3532r_ext_info), + AD3530R_CHAN(4, ad3532r_ext_info), + AD3530R_CHAN(5, ad3532r_ext_info), + AD3530R_CHAN(6, ad3532r_ext_info), + AD3530R_CHAN(7, ad3532r_ext_info), + AD3530R_CHAN(8, ad3532r_ext_info), + AD3530R_CHAN(9, ad3532r_ext_info), + AD3530R_CHAN(10, ad3532r_ext_info), + AD3530R_CHAN(11, ad3532r_ext_info), + AD3530R_CHAN(12, ad3532r_ext_info), + AD3530R_CHAN(13, ad3532r_ext_info), + AD3530R_CHAN(14, ad3532r_ext_info), + AD3530R_CHAN(15, ad3532r_ext_info), +}; + +static const unsigned int ad3530r_if_config[] = { + AD3530R_INTERFACE_CONFIG_A, +}; + +static const unsigned int ad3530r_out_ctrl[] = { + AD3530R_OUTPUT_CONTROL_0, +}; + +static const unsigned int ad3530r_ref_ctrl[] = { + AD3530R_REFERENCE_CONTROL_0, +}; + +static const unsigned int ad3530r_op_mode[] = { + AD3530R_OUTPUT_OPERATING_MODE_0, + AD3530R_OUTPUT_OPERATING_MODE_1, +}; + +static const unsigned int ad3531r_op_mode[] = { + AD3530R_OUTPUT_OPERATING_MODE_0, +}; + +static const unsigned int ad3532r_if_config[] = { + AD3532R_INTERFACE_CONFIG_A_0, + AD3532R_INTERFACE_CONFIG_A_1, +}; + +static const unsigned int ad3532r_out_ctrl[] = { + AD3532R_OUTPUT_CONTROL_0, + AD3532R_OUTPUT_CONTROL_1, +}; + +static const unsigned int ad3532r_ref_ctrl[] = { + AD3532R_REFERENCE_CONTROL_0, + AD3532R_REFERENCE_CONTROL_1, +}; + +static const unsigned int ad3532r_op_mode[] = { + AD3532R_OUTPUT_OPERATING_MODE_0, + AD3532R_OUTPUT_OPERATING_MODE_1, + AD3532R_OUTPUT_OPERATING_MODE_2, + AD3532R_OUTPUT_OPERATING_MODE_3, +}; + +static const struct regmap_config ad3530r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD3530R_MAX_REG_ADDR, +}; + +static const struct regmap_config ad3532r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD3532R_MAX_REG_ADDR, +}; + static const struct ad3530r_chip_info ad3530_chip = { .name = "ad3530", .channels = ad3530r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = false, }; static const struct ad3530r_chip_info ad3530r_chip = { .name = "ad3530r", .channels = ad3530r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = true, }; static const struct ad3530r_chip_info ad3531_chip = { .name = "ad3531", .channels = ad3531r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3531r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), .internal_ref_support = false, }; static const struct ad3530r_chip_info ad3531r_chip = { .name = "ad3531r", .channels = ad3531r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3531r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), + .internal_ref_support = true, +}; + +static const struct ad3530r_chip_info ad3532_chip = { + .name = "ad3532", + .channels = ad3532r_channels, + .regmap_config = &ad3532r_regmap_config, + .num_channels = ARRAY_SIZE(ad3532r_channels), + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .interface_config_a = ad3532r_if_config, + .output_control = ad3532r_out_ctrl, + .reference_control = ad3532r_ref_ctrl, + .op_mode = ad3532r_op_mode, + .num_banks = ARRAY_SIZE(ad3532r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), + .internal_ref_support = false, +}; + +static const struct ad3530r_chip_info ad3532r_chip = { + .name = "ad3532r", + .channels = ad3532r_channels, + .regmap_config = &ad3532r_regmap_config, + .num_channels = ARRAY_SIZE(ad3532r_channels), + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .interface_config_a = ad3532r_if_config, + .output_control = ad3532r_out_ctrl, + .reference_control = ad3532r_ref_ctrl, + .op_mode = ad3532r_op_mode, + .num_banks = ARRAY_SIZE(ad3532r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), .internal_ref_support = true, }; +static int ad3530r_set_reg_bank_bits(const struct ad3530r_state *st, + const unsigned int *regs, + unsigned int num_regs, + unsigned int mask) +{ + int ret; + + for (unsigned int i = 0; i < num_regs; i++) { + ret = regmap_set_bits(st->regmap, regs[i], mask); + if (ret) + return ret; + } + + return 0; +} + +static int ad3530r_write_reg_banks(const struct ad3530r_state *st, + const unsigned int *regs, + unsigned int num_regs, + unsigned int val) +{ + int ret; + + for (unsigned int i = 0; i < num_regs; i++) { + ret = regmap_write(st->regmap, regs[i], val); + if (ret) + return ret; + } + + return 0; +} + static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) { + const struct ad3530r_chip_info *chip_info = st->chip_info; struct device *dev = regmap_get_device(st->regmap); struct gpio_desc *reset_gpio; - int i, ret; u8 range_multiplier, val; + int ret; reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) @@ -384,8 +670,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) gpiod_set_value_cansleep(reset_gpio, 0); } else { /* Perform software reset */ - ret = regmap_update_bits(st->regmap, AD3530R_INTERFACE_CONFIG_A, - AD3530R_SW_RESET, AD3530R_SW_RESET); + ret = ad3530r_set_reg_bank_bits(st, chip_info->interface_config_a, + chip_info->num_banks, + AD3530R_SW_RESET); if (ret) return ret; } @@ -394,8 +681,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) range_multiplier = 1; if (device_property_read_bool(dev, "adi,range-double")) { - ret = regmap_set_bits(st->regmap, AD3530R_OUTPUT_CONTROL_0, - AD3530R_OUTPUT_CONTROL_RANGE); + ret = ad3530r_set_reg_bank_bits(st, chip_info->output_control, + chip_info->num_banks, + AD3530R_OUTPUT_CONTROL_RANGE); if (ret) return ret; @@ -405,8 +693,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) if (external_vref_uV) { st->vref_mV = range_multiplier * external_vref_uV / MILLI; } else { - ret = regmap_set_bits(st->regmap, AD3530R_REFERENCE_CONTROL_0, - AD3530R_REFERENCE_CONTROL_SEL); + ret = ad3530r_set_reg_bank_bits(st, chip_info->reference_control, + chip_info->num_banks, + AD3530R_REFERENCE_CONTROL_SEL); if (ret) return ret; @@ -419,18 +708,12 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) | FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP); - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_0, val); + ret = ad3530r_write_reg_banks(st, st->chip_info->op_mode, + st->chip_info->num_op_mode_regs, val); if (ret) return ret; - if (st->chip_info->num_channels > 4) { - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_1, - val); - if (ret) - return ret; - } - - for (i = 0; i < st->chip_info->num_channels; i++) + for (unsigned int i = 0; i < st->chip_info->num_channels; i++) st->chan[i].powerdown_mode = AD3530R_POWERDOWN_32K; st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_LOW); @@ -441,12 +724,6 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) return 0; } -static const struct regmap_config ad3530r_regmap_config = { - .reg_bits = 16, - .val_bits = 8, - .max_register = AD3530R_MAX_REG_ADDR, -}; - static const struct iio_info ad3530r_info = { .read_raw = ad3530r_read_raw, .write_raw = ad3530r_write_raw, @@ -467,7 +744,11 @@ static int ad3530r_probe(struct spi_device *spi) st = iio_priv(indio_dev); - st->regmap = devm_regmap_init_spi(spi, &ad3530r_regmap_config); + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + st->regmap = devm_regmap_init_spi(spi, st->chip_info->regmap_config); if (IS_ERR(st->regmap)) return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to init regmap"); @@ -476,10 +757,6 @@ static int ad3530r_probe(struct spi_device *spi) if (ret) return ret; - st->chip_info = spi_get_device_match_data(spi); - if (!st->chip_info) - return -ENODEV; - ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), regulators); if (ret) @@ -509,10 +786,12 @@ static int ad3530r_probe(struct spi_device *spi) } static const struct spi_device_id ad3530r_id[] = { - { "ad3530", (kernel_ulong_t)&ad3530_chip }, - { "ad3530r", (kernel_ulong_t)&ad3530r_chip }, - { "ad3531", (kernel_ulong_t)&ad3531_chip }, - { "ad3531r", (kernel_ulong_t)&ad3531r_chip }, + { .name = "ad3530", .driver_data = (kernel_ulong_t)&ad3530_chip }, + { .name = "ad3530r", .driver_data = (kernel_ulong_t)&ad3530r_chip }, + { .name = "ad3531", .driver_data = (kernel_ulong_t)&ad3531_chip }, + { .name = "ad3531r", .driver_data = (kernel_ulong_t)&ad3531r_chip }, + { .name = "ad3532", .driver_data = (kernel_ulong_t)&ad3532_chip }, + { .name = "ad3532r", .driver_data = (kernel_ulong_t)&ad3532r_chip }, { } }; MODULE_DEVICE_TABLE(spi, ad3530r_id); @@ -522,6 +801,8 @@ static const struct of_device_id ad3530r_of_match[] = { { .compatible = "adi,ad3530r", .data = &ad3530r_chip }, { .compatible = "adi,ad3531", .data = &ad3531_chip }, { .compatible = "adi,ad3531r", .data = &ad3531r_chip }, + { .compatible = "adi,ad3532", .data = &ad3532_chip }, + { .compatible = "adi,ad3532r", .data = &ad3532r_chip }, { } }; MODULE_DEVICE_TABLE(of, ad3530r_of_match); diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index b5ec33f5c317..298f3a703571 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -920,22 +920,22 @@ static int ad5064_spi_probe(struct spi_device *spi) } static const struct spi_device_id ad5064_spi_ids[] = { - {"ad5024", ID_AD5024}, - {"ad5025", ID_AD5025}, - {"ad5044", ID_AD5044}, - {"ad5045", ID_AD5045}, - {"ad5064", ID_AD5064}, - {"ad5064-1", ID_AD5064_1}, - {"ad5065", ID_AD5065}, - {"ad5628-1", ID_AD5628_1}, - {"ad5628-2", ID_AD5628_2}, - {"ad5648-1", ID_AD5648_1}, - {"ad5648-2", ID_AD5648_2}, - {"ad5666-1", ID_AD5666_1}, - {"ad5666-2", ID_AD5666_2}, - {"ad5668-1", ID_AD5668_1}, - {"ad5668-2", ID_AD5668_2}, - {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */ + { .name = "ad5024", .driver_data = ID_AD5024 }, + { .name = "ad5025", .driver_data = ID_AD5025 }, + { .name = "ad5044", .driver_data = ID_AD5044 }, + { .name = "ad5045", .driver_data = ID_AD5045 }, + { .name = "ad5064", .driver_data = ID_AD5064 }, + { .name = "ad5064-1", .driver_data = ID_AD5064_1 }, + { .name = "ad5065", .driver_data = ID_AD5065 }, + { .name = "ad5628-1", .driver_data = ID_AD5628_1 }, + { .name = "ad5628-2", .driver_data = ID_AD5628_2 }, + { .name = "ad5648-1", .driver_data = ID_AD5648_1 }, + { .name = "ad5648-2", .driver_data = ID_AD5648_2 }, + { .name = "ad5666-1", .driver_data = ID_AD5666_1 }, + { .name = "ad5666-2", .driver_data = ID_AD5666_2 }, + { .name = "ad5668-1", .driver_data = ID_AD5668_1 }, + { .name = "ad5668-2", .driver_data = ID_AD5668_2 }, + { .name = "ad5668-3", .driver_data = ID_AD5668_2 }, /* similar enough to ad5668-2 */ { } }; MODULE_DEVICE_TABLE(spi, ad5064_spi_ids); diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index 20316fd568e6..c1553f18a26a 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -523,14 +523,14 @@ static void ad5360_remove(struct spi_device *spi) } static const struct spi_device_id ad5360_ids[] = { - { "ad5360", ID_AD5360 }, - { "ad5361", ID_AD5361 }, - { "ad5362", ID_AD5362 }, - { "ad5363", ID_AD5363 }, - { "ad5370", ID_AD5370 }, - { "ad5371", ID_AD5371 }, - { "ad5372", ID_AD5372 }, - { "ad5373", ID_AD5373 }, + { .name = "ad5360", .driver_data = ID_AD5360 }, + { .name = "ad5361", .driver_data = ID_AD5361 }, + { .name = "ad5362", .driver_data = ID_AD5362 }, + { .name = "ad5363", .driver_data = ID_AD5363 }, + { .name = "ad5370", .driver_data = ID_AD5370 }, + { .name = "ad5371", .driver_data = ID_AD5371 }, + { .name = "ad5372", .driver_data = ID_AD5372 }, + { .name = "ad5373", .driver_data = ID_AD5373 }, { } }; MODULE_DEVICE_TABLE(spi, ad5360_ids); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 2e587bdd3214..4a5ad419f4ea 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -446,22 +446,22 @@ static int ad5380_spi_probe(struct spi_device *spi) } static const struct spi_device_id ad5380_spi_ids[] = { - { "ad5380-3", ID_AD5380_3 }, - { "ad5380-5", ID_AD5380_5 }, - { "ad5381-3", ID_AD5381_3 }, - { "ad5381-5", ID_AD5381_5 }, - { "ad5382-3", ID_AD5382_3 }, - { "ad5382-5", ID_AD5382_5 }, - { "ad5383-3", ID_AD5383_3 }, - { "ad5383-5", ID_AD5383_5 }, - { "ad5384-3", ID_AD5380_3 }, - { "ad5384-5", ID_AD5380_5 }, - { "ad5390-3", ID_AD5390_3 }, - { "ad5390-5", ID_AD5390_5 }, - { "ad5391-3", ID_AD5391_3 }, - { "ad5391-5", ID_AD5391_5 }, - { "ad5392-3", ID_AD5392_3 }, - { "ad5392-5", ID_AD5392_5 }, + { .name = "ad5380-3", .driver_data = ID_AD5380_3 }, + { .name = "ad5380-5", .driver_data = ID_AD5380_5 }, + { .name = "ad5381-3", .driver_data = ID_AD5381_3 }, + { .name = "ad5381-5", .driver_data = ID_AD5381_5 }, + { .name = "ad5382-3", .driver_data = ID_AD5382_3 }, + { .name = "ad5382-5", .driver_data = ID_AD5382_5 }, + { .name = "ad5383-3", .driver_data = ID_AD5383_3 }, + { .name = "ad5383-5", .driver_data = ID_AD5383_5 }, + { .name = "ad5384-3", .driver_data = ID_AD5380_3 }, + { .name = "ad5384-5", .driver_data = ID_AD5380_5 }, + { .name = "ad5390-3", .driver_data = ID_AD5390_3 }, + { .name = "ad5390-5", .driver_data = ID_AD5390_5 }, + { .name = "ad5391-3", .driver_data = ID_AD5391_3 }, + { .name = "ad5391-5", .driver_data = ID_AD5391_5 }, + { .name = "ad5392-3", .driver_data = ID_AD5392_3 }, + { .name = "ad5392-5", .driver_data = ID_AD5392_5 }, { } }; MODULE_DEVICE_TABLE(spi, ad5380_spi_ids); diff --git a/drivers/iio/dac/ad5446-spi.c b/drivers/iio/dac/ad5446-spi.c index 54bd0e113f40..85bb8829046a 100644 --- a/drivers/iio/dac/ad5446-spi.c +++ b/drivers/iio/dac/ad5446-spi.c @@ -163,38 +163,38 @@ static const struct ad5446_chip_info ad5662_chip_info = { }; static const struct spi_device_id ad5446_spi_ids[] = { - {"ad5300", (kernel_ulong_t)&ad5300_chip_info}, - {"ad5310", (kernel_ulong_t)&ad5310_chip_info}, - {"ad5320", (kernel_ulong_t)&ad5320_chip_info}, - {"ad5444", (kernel_ulong_t)&ad5444_chip_info}, - {"ad5446", (kernel_ulong_t)&ad5446_chip_info}, - {"ad5450", (kernel_ulong_t)&ad5450_chip_info}, - {"ad5451", (kernel_ulong_t)&ad5451_chip_info}, - {"ad5452", (kernel_ulong_t)&ad5444_chip_info}, /* ad5452 is compatible to the ad5444 */ - {"ad5453", (kernel_ulong_t)&ad5446_chip_info}, /* ad5453 is compatible to the ad5446 */ - {"ad5512a", (kernel_ulong_t)&ad5512a_chip_info}, - {"ad5541a", (kernel_ulong_t)&ad5541a_chip_info}, - {"ad5542", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5542 are compatible */ - {"ad5542a", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5542a are compatible */ - {"ad5543", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5543 are compatible */ - {"ad5553", (kernel_ulong_t)&ad5553_chip_info}, - {"ad5600", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5600 are compatible */ - {"ad5601", (kernel_ulong_t)&ad5601_chip_info}, - {"ad5611", (kernel_ulong_t)&ad5611_chip_info}, - {"ad5621", (kernel_ulong_t)&ad5621_chip_info}, - {"ad5641", (kernel_ulong_t)&ad5641_chip_info}, - {"ad5620-2500", (kernel_ulong_t)&ad5620_2500_chip_info}, /* AD5620/40/60: */ + { .name = "ad5300", .driver_data = (kernel_ulong_t)&ad5300_chip_info }, + { .name = "ad5310", .driver_data = (kernel_ulong_t)&ad5310_chip_info }, + { .name = "ad5320", .driver_data = (kernel_ulong_t)&ad5320_chip_info }, + { .name = "ad5444", .driver_data = (kernel_ulong_t)&ad5444_chip_info }, + { .name = "ad5446", .driver_data = (kernel_ulong_t)&ad5446_chip_info }, + { .name = "ad5450", .driver_data = (kernel_ulong_t)&ad5450_chip_info }, + { .name = "ad5451", .driver_data = (kernel_ulong_t)&ad5451_chip_info }, + { .name = "ad5452", .driver_data = (kernel_ulong_t)&ad5444_chip_info }, /* ad5452 is compatible to the ad5444 */ + { .name = "ad5453", .driver_data = (kernel_ulong_t)&ad5446_chip_info }, /* ad5453 is compatible to the ad5446 */ + { .name = "ad5512a", .driver_data = (kernel_ulong_t)&ad5512a_chip_info }, + { .name = "ad5541a", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, + { .name = "ad5542", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5542 are compatible */ + { .name = "ad5542a", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5542a are compatible */ + { .name = "ad5543", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5543 are compatible */ + { .name = "ad5553", .driver_data = (kernel_ulong_t)&ad5553_chip_info }, + { .name = "ad5600", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5600 are compatible */ + { .name = "ad5601", .driver_data = (kernel_ulong_t)&ad5601_chip_info }, + { .name = "ad5611", .driver_data = (kernel_ulong_t)&ad5611_chip_info }, + { .name = "ad5621", .driver_data = (kernel_ulong_t)&ad5621_chip_info }, + { .name = "ad5641", .driver_data = (kernel_ulong_t)&ad5641_chip_info }, + { .name = "ad5620-2500", .driver_data = (kernel_ulong_t)&ad5620_2500_chip_info }, /* AD5620/40/60: */ /* part numbers may look differently */ - {"ad5620-1250", (kernel_ulong_t)&ad5620_1250_chip_info}, - {"ad5640-2500", (kernel_ulong_t)&ad5640_2500_chip_info}, - {"ad5640-1250", (kernel_ulong_t)&ad5640_1250_chip_info}, - {"ad5660-2500", (kernel_ulong_t)&ad5660_2500_chip_info}, - {"ad5660-1250", (kernel_ulong_t)&ad5660_1250_chip_info}, - {"ad5662", (kernel_ulong_t)&ad5662_chip_info}, - {"dac081s101", (kernel_ulong_t)&ad5300_chip_info}, /* compatible Texas Instruments chips */ - {"dac101s101", (kernel_ulong_t)&ad5310_chip_info}, - {"dac121s101", (kernel_ulong_t)&ad5320_chip_info}, - {"dac7512", (kernel_ulong_t)&ad5320_chip_info}, + { .name = "ad5620-1250", .driver_data = (kernel_ulong_t)&ad5620_1250_chip_info }, + { .name = "ad5640-2500", .driver_data = (kernel_ulong_t)&ad5640_2500_chip_info }, + { .name = "ad5640-1250", .driver_data = (kernel_ulong_t)&ad5640_1250_chip_info }, + { .name = "ad5660-2500", .driver_data = (kernel_ulong_t)&ad5660_2500_chip_info }, + { .name = "ad5660-1250", .driver_data = (kernel_ulong_t)&ad5660_1250_chip_info }, + { .name = "ad5662", .driver_data = (kernel_ulong_t)&ad5662_chip_info }, + { .name = "dac081s101", .driver_data = (kernel_ulong_t)&ad5300_chip_info }, /* compatible Texas Instruments chips */ + { .name = "dac101s101", .driver_data = (kernel_ulong_t)&ad5310_chip_info }, + { .name = "dac121s101", .driver_data = (kernel_ulong_t)&ad5320_chip_info }, + { .name = "dac7512", .driver_data = (kernel_ulong_t)&ad5320_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5446_spi_ids); diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index d8c325260259..19e66bc69676 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -330,13 +330,13 @@ static void ad5449_spi_remove(struct spi_device *spi) } static const struct spi_device_id ad5449_spi_ids[] = { - { "ad5415", ID_AD5449 }, - { "ad5426", ID_AD5426 }, - { "ad5429", ID_AD5429 }, - { "ad5432", ID_AD5432 }, - { "ad5439", ID_AD5439 }, - { "ad5443", ID_AD5443 }, - { "ad5449", ID_AD5449 }, + { .name = "ad5415", .driver_data = ID_AD5449 }, + { .name = "ad5426", .driver_data = ID_AD5426 }, + { .name = "ad5429", .driver_data = ID_AD5429 }, + { .name = "ad5432", .driver_data = ID_AD5432 }, + { .name = "ad5439", .driver_data = ID_AD5439 }, + { .name = "ad5443", .driver_data = ID_AD5443 }, + { .name = "ad5449", .driver_data = ID_AD5449 }, { } }; MODULE_DEVICE_TABLE(spi, ad5449_spi_ids); diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 5e586185d857..ad1a74678cf2 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -319,8 +319,8 @@ static int ad5504_probe(struct spi_device *spi) } static const struct spi_device_id ad5504_id[] = { - {"ad5504", ID_AD5504}, - {"ad5501", ID_AD5501}, + { .name = "ad5504", .driver_data = ID_AD5504 }, + { .name = "ad5501", .driver_data = ID_AD5501 }, { } }; MODULE_DEVICE_TABLE(spi, ad5504_id); diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 13aefe769bad..56aea4b1c167 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -260,12 +260,12 @@ static int ad5624r_probe(struct spi_device *spi) } static const struct spi_device_id ad5624r_id[] = { - {"ad5624r3", ID_AD5624R3}, - {"ad5644r3", ID_AD5644R3}, - {"ad5664r3", ID_AD5664R3}, - {"ad5624r5", ID_AD5624R5}, - {"ad5644r5", ID_AD5644R5}, - {"ad5664r5", ID_AD5664R5}, + { .name = "ad5624r3", .driver_data = ID_AD5624R3 }, + { .name = "ad5644r3", .driver_data = ID_AD5644R3 }, + { .name = "ad5664r3", .driver_data = ID_AD5664R3 }, + { .name = "ad5624r5", .driver_data = ID_AD5624R5 }, + { .name = "ad5644r5", .driver_data = ID_AD5644R5 }, + { .name = "ad5664r5", .driver_data = ID_AD5664R5 }, { } }; MODULE_DEVICE_TABLE(spi, ad5624r_id); diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 8abfaf8f0c46..69cd9753f339 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -1,69 +1,109 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5672R, AD5674R, AD5676, AD5676R, AD5679R, - * AD5681R, AD5682R, AD5683, AD5683R, AD5684, - * AD5684R, AD5685R, AD5686, AD5686R - * Digital to analog converters driver + * SPI driver for AD5686 and similar Digital to Analog Converters * * Copyright 2018 Analog Devices Inc. */ #include <linux/array_size.h> +#include <linux/bitfield.h> #include <linux/errno.h> #include <linux/module.h> +#include <linux/overflow.h> #include <linux/spi/spi.h> #include <asm/byteorder.h> #include "ad5686.h" +/** + * struct ad5686_spi_data - SPI bus specific data + * @msg: SPI message used for transfers + * @size: number of transfers currently in the message + * @capacity: maximum number of transfers that can be added to the message + * @xfers: array of SPI transfers, allocated with the provided capacity + */ +struct ad5686_spi_data { + struct spi_message msg; + unsigned int size; + unsigned int capacity; + struct spi_transfer xfers[] __counted_by(capacity); +}; + static int ad5686_spi_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val) { - struct spi_device *spi = to_spi_device(st->dev); - u8 tx_len, *buf; - + struct ad5686_spi_data *bus_data = st->bus_data; + struct spi_transfer *xfer; + + if (bus_data->size >= bus_data->capacity) + return -E2BIG; + + /* + * This function stores spi transfers to a spi message to be sent over + * the bus when sync() op is called. If there are already transfers in + * the spi message, set the cs_change flag on the last transfer to + * ensure that the chip select is deasserted between transfers. If this + * is the first transfer, initialize the spi message. Later on, the + * current transfer is added to the message with spi_message_add_tail(). + */ + if (bus_data->size) + bus_data->xfers[bus_data->size - 1].cs_change = 1; + else + spi_message_init(&bus_data->msg); + + xfer = &bus_data->xfers[bus_data->size]; + auto buf = &st->data[bus_data->size]; switch (st->chip_info->regmap_type) { case AD5310_REGMAP: - st->data[0].d16 = cpu_to_be16(AD5310_CMD(cmd) | - val); - buf = &st->data[0].d8[0]; - tx_len = 2; + buf->d16 = cpu_to_be16(FIELD_PREP(AD5310_CMD_MSK, cmd) | + FIELD_PREP(AD5310_DATA_MSK, val)); + *xfer = (struct spi_transfer) { + .tx_buf = &buf->d16, + .len = sizeof(buf->d16), + }; break; case AD5683_REGMAP: - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5683_DATA(val)); - buf = &st->data[0].d8[1]; - tx_len = 3; + buf->d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5683_DATA_MSK, val)); + *xfer = (struct spi_transfer) { + .tx_buf = &buf->d8[1], + .len = sizeof(buf->d8) - 1, + }; break; case AD5686_REGMAP: - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr) | - val); - buf = &st->data[0].d8[1]; - tx_len = 3; + buf->d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr) | + FIELD_PREP(AD5686_DATA_MSK, val)); + *xfer = (struct spi_transfer) { + .tx_buf = &buf->d8[1], + .len = sizeof(buf->d8) - 1, + }; break; default: return -EINVAL; } - return spi_write(spi, buf, tx_len); + spi_message_add_tail(xfer, &bus_data->msg); + bus_data->size++; + + return 0; +} + +static int ad5686_spi_sync(struct ad5686_state *st) +{ + struct spi_device *spi = to_spi_device(st->dev); + struct ad5686_spi_data *bus_data = st->bus_data; + + bus_data->size = 0; /* always reset, even on sync failure */ + return spi_sync(spi, &bus_data->msg); } static int ad5686_spi_read(struct ad5686_state *st, u8 addr) { - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .tx_buf = &st->data[1].d8[1], - .rx_buf = &st->data[2].d8[1], - .len = 3, - }, - }; struct spi_device *spi = to_spi_device(st->dev); + struct ad5686_spi_data *bus_data = st->bus_data; + struct spi_transfer *xfer = &bus_data->xfers[0]; u8 cmd = 0; int ret; @@ -80,12 +120,25 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr) return -EINVAL; } - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr)); - st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr)); + st->data[1].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, AD5686_CMD_NOOP)); + + xfer[0] = (struct spi_transfer) { + .tx_buf = &st->data[0].d8[1], + .len = sizeof(st->data[0].d8) - 1, + .cs_change = 1, + }; + xfer[1] = (struct spi_transfer) { + .tx_buf = &st->data[1].d8[1], + .rx_buf = &st->data[2].d8[1], + .len = sizeof(st->data[1].d8) - 1, + }; + + spi_message_init_with_transfers(&bus_data->msg, xfer, 2); - ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); - if (ret < 0) + ret = spi_sync(spi, &bus_data->msg); + if (ret) return ret; return be32_to_cpu(st->data[2].d32); @@ -94,20 +147,42 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr) static const struct ad5686_bus_ops ad5686_spi_ops = { .write = ad5686_spi_write, .read = ad5686_spi_read, + .sync = ad5686_spi_sync, }; static int ad5686_spi_probe(struct spi_device *spi) { - return ad5686_probe(&spi->dev, spi_get_device_match_data(spi), - spi->modalias, &ad5686_spi_ops); + const struct ad5686_chip_info *info; + struct ad5686_spi_data *bus_data; + struct device *dev = &spi->dev; + unsigned int capacity; + + info = spi_get_device_match_data(spi); + if (!info) + return -ENODATA; + + /* read operation requires at least 2 transfers */ + capacity = max(info->num_channels, 2); + bus_data = devm_kzalloc(dev, struct_size(bus_data, xfers, capacity), + GFP_KERNEL); + if (!bus_data) + return -ENOMEM; + + bus_data->capacity = capacity; + + return ad5686_probe(dev, info, spi->modalias, &ad5686_spi_ops, bus_data); } static const struct spi_device_id ad5686_spi_id[] = { { .name = "ad5310r", .driver_data = (kernel_ulong_t)&ad5310r_chip_info }, + { .name = "ad5313r", .driver_data = (kernel_ulong_t)&ad5338r_chip_info }, + { .name = "ad5317r", .driver_data = (kernel_ulong_t)&ad5317r_chip_info }, { .name = "ad5672r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info }, + { .name = "ad5674", .driver_data = (kernel_ulong_t)&ad5674_chip_info }, { .name = "ad5674r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info }, { .name = "ad5676", .driver_data = (kernel_ulong_t)&ad5676_chip_info }, { .name = "ad5676r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info }, + { .name = "ad5679", .driver_data = (kernel_ulong_t)&ad5679_chip_info }, { .name = "ad5679r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info }, { .name = "ad5681r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info }, { .name = "ad5682r", .driver_data = (kernel_ulong_t)&ad5682r_chip_info }, @@ -119,16 +194,24 @@ static const struct spi_device_id ad5686_spi_id[] = { { .name = "ad5685r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info }, { .name = "ad5686", .driver_data = (kernel_ulong_t)&ad5686_chip_info }, { .name = "ad5686r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info }, + { .name = "ad5687", .driver_data = (kernel_ulong_t)&ad5687_chip_info }, + { .name = "ad5687r", .driver_data = (kernel_ulong_t)&ad5687r_chip_info }, + { .name = "ad5689", .driver_data = (kernel_ulong_t)&ad5689_chip_info }, + { .name = "ad5689r", .driver_data = (kernel_ulong_t)&ad5689r_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5686_spi_id); static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5310r", .data = &ad5310r_chip_info }, + { .compatible = "adi,ad5313r", .data = &ad5338r_chip_info }, + { .compatible = "adi,ad5317r", .data = &ad5317r_chip_info }, { .compatible = "adi,ad5672r", .data = &ad5672r_chip_info }, + { .compatible = "adi,ad5674", .data = &ad5674_chip_info }, { .compatible = "adi,ad5674r", .data = &ad5674r_chip_info }, { .compatible = "adi,ad5676", .data = &ad5676_chip_info }, { .compatible = "adi,ad5676r", .data = &ad5676r_chip_info }, + { .compatible = "adi,ad5679", .data = &ad5679_chip_info }, { .compatible = "adi,ad5679r", .data = &ad5679r_chip_info }, { .compatible = "adi,ad5681r", .data = &ad5681r_chip_info }, { .compatible = "adi,ad5682r", .data = &ad5682r_chip_info }, @@ -139,6 +222,10 @@ static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5685r", .data = &ad5685r_chip_info }, { .compatible = "adi,ad5686", .data = &ad5686_chip_info }, { .compatible = "adi,ad5686r", .data = &ad5686r_chip_info }, + { .compatible = "adi,ad5687", .data = &ad5687_chip_info }, + { .compatible = "adi,ad5687r", .data = &ad5687r_chip_info }, + { .compatible = "adi,ad5689", .data = &ad5689_chip_info }, + { .compatible = "adi,ad5689r", .data = &ad5689r_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad5686_of_match); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 5840fda4b011..0cc1c9c22a28 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5686R, AD5685R, AD5684R Digital to analog converters driver + * Core driver for AD5686 and similar Digital to Analog Converters * * Copyright 2011 Analog Devices Inc. */ @@ -8,15 +8,27 @@ #include <linux/array_size.h> #include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> #include <linux/errno.h> #include <linux/export.h> +#include <linux/gpio/consumer.h> #include <linux/kstrtox.h> +#include <linux/math64.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/sysfs.h> +#include <linux/units.h> #include <linux/wordpart.h> +#include <linux/iio/buffer.h> #include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> #include "ad5686.h" @@ -31,8 +43,9 @@ static int ad5310_control_sync(struct ad5686_state *st) unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, - FIELD_PREP(AD5310_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5310_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); + FIELD_PREP(AD5310_DATA_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5310_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) | + FIELD_PREP(AD5310_DATA_GAIN_MSK, st->double_scale ? 1 : 0)); } static int ad5683_control_sync(struct ad5686_state *st) @@ -40,8 +53,9 @@ static int ad5683_control_sync(struct ad5686_state *st) unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, - FIELD_PREP(AD5683_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5683_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); + FIELD_PREP(AD5683_DATA_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5683_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) | + FIELD_PREP(AD5683_DATA_GAIN_MSK, st->double_scale ? 1 : 0)); } static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan) @@ -173,20 +187,25 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, struct ad5686_state *st = iio_priv(indio_dev); int ret; + guard(mutex)(&st->lock); + switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&st->lock); ret = ad5686_read(st, chan->address); - mutex_unlock(&st->lock); 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_mv; - *val2 = chan->scan_type.realbits; - return IIO_VAL_FRACTIONAL_LOG2; + if (st->double_scale) { + *val = st->scale_avail[2]; + *val2 = st->scale_avail[3]; + } else { + *val = st->scale_avail[0]; + *val2 = st->scale_avail[1]; + } + return IIO_VAL_INT_PLUS_NANO; } return -EINVAL; } @@ -198,28 +217,104 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, long mask) { struct ad5686_state *st = iio_priv(indio_dev); + bool double_scale; int ret; + guard(mutex)(&st->lock); + switch (mask) { case IIO_CHAN_INFO_RAW: if (val >= (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; - mutex_lock(&st->lock); - ret = ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, val << chan->scan_type.shift); - mutex_unlock(&st->lock); - break; + return ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, val << chan->scan_type.shift); + case IIO_CHAN_INFO_SCALE: + if (val == st->scale_avail[0] && val2 == st->scale_avail[1]) + double_scale = false; + else if (val == st->scale_avail[2] && val2 == st->scale_avail[3]) + double_scale = true; + else + return -EINVAL; + + if (st->double_scale == double_scale) + return 0; /* no change */ + + if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio) + return -EINVAL; /* GAIN pin is board-strapped */ + + st->double_scale = double_scale; + switch (st->chip_info->regmap_type) { + case AD5310_REGMAP: + ret = ad5310_control_sync(st); + break; + case AD5683_REGMAP: + ret = ad5683_control_sync(st); + break; + case AD5686_REGMAP: + ret = gpiod_set_value_cansleep(st->gain_gpio, + st->double_scale ? 1 : 0); + break; + default: + ret = -EINVAL; + } + if (ret) + st->double_scale = !double_scale; /* revert on failure */ + return ret; default: - ret = -EINVAL; + return -EINVAL; } +} - return ret; +static int ad5686_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_RAW: + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int ad5686_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad5686_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_NANO; + + if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio) { + /* + * GAIN pin is board-strapped, so only the current + * scale is available. + */ + *vals = st->double_scale ? &st->scale_avail[2] : + &st->scale_avail[0]; + *length = 2; + return IIO_AVAIL_LIST; + } + + *vals = st->scale_avail; + *length = ARRAY_SIZE(st->scale_avail); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } } static const struct iio_info ad5686_info = { .read_raw = ad5686_read_raw, .write_raw = ad5686_write_raw, + .write_raw_get_fmt = ad5686_write_raw_get_fmt, + .read_avail = ad5686_read_avail, }; static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { @@ -241,7 +336,9 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .channel = chan, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),\ .address = addr, \ + .scan_index = chan, \ .scan_type = { \ .sign = 'u', \ .realbits = (bits), \ @@ -312,8 +409,11 @@ DECLARE_AD5683_CHANNELS(ad5683r_channels, 16, 0); /* dual-channel */ DECLARE_AD5338_CHANNELS(ad5337r_channels, 8, 8); DECLARE_AD5338_CHANNELS(ad5338r_channels, 10, 6); +DECLARE_AD5338_CHANNELS(ad5687r_channels, 12, 4); +DECLARE_AD5338_CHANNELS(ad5689r_channels, 16, 0); /* quad-channel */ +DECLARE_AD5686_CHANNELS(ad5317r_channels, 10, 6); DECLARE_AD5686_CHANNELS(ad5684r_channels, 12, 4); DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); DECLARE_AD5686_CHANNELS(ad5686r_channels, 16, 0); @@ -389,6 +489,44 @@ const struct ad5686_chip_info ad5338r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5338r_chip_info, "IIO_AD5686"); +const struct ad5686_chip_info ad5687_chip_info = { + .channels = ad5687r_channels, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5687_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5687r_chip_info = { + .channels = ad5687r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5687r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5689_chip_info = { + .channels = ad5689r_channels, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5689_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5689r_chip_info = { + .channels = ad5689r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5689r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5317r_chip_info = { + .channels = ad5317r_channels, + .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5317r_chip_info, "IIO_AD5686"); + const struct ad5686_chip_info ad5684_chip_info = { .channels = ad5684r_channels, .num_channels = 4, @@ -450,6 +588,13 @@ const struct ad5686_chip_info ad5676r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5676r_chip_info, "IIO_AD5686"); +const struct ad5686_chip_info ad5674_chip_info = { + .channels = ad5674r_channels, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5674_chip_info, "IIO_AD5686"); + const struct ad5686_chip_info ad5674r_chip_info = { .channels = ad5674r_channels, .int_vref_mv = 2500, @@ -458,6 +603,13 @@ const struct ad5686_chip_info ad5674r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5674r_chip_info, "IIO_AD5686"); +const struct ad5686_chip_info ad5679_chip_info = { + .channels = ad5679r_channels, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5679_chip_info, "IIO_AD5686"); + const struct ad5686_chip_info ad5679r_chip_info = { .channels = ad5679r_channels, .int_vref_mv = 2500, @@ -466,10 +618,74 @@ const struct ad5686_chip_info ad5679r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686"); +static void ad5686_init_scale_avail(struct ad5686_state *st) +{ + int realbits = st->chip_info->channels[0].scan_type.realbits; + s64 tmp = 2ULL * st->vref_mv * NANO >> realbits; + + st->scale_avail[2] = div_s64_rem(tmp, NANO, &st->scale_avail[3]); + st->scale_avail[0] = div_s64_rem(tmp >> 1, NANO, &st->scale_avail[1]); +} + +static void do_ad5686_trigger_handler(struct iio_dev *indio_dev) +{ + struct ad5686_state *st = iio_priv(indio_dev); + u16 val[AD5686_MAX_CHANNELS] = { }; + unsigned int scan_count, ch, i; + bool async_update; + u8 cmd; + + if (iio_pop_from_buffer(indio_dev->buffer, val)) + return; + + guard(mutex)(&st->lock); + + scan_count = bitmap_weight(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + async_update = st->ldac_gpio && scan_count > 1; + if (async_update) { + /* use LDAC to update all channels simultaneously */ + cmd = AD5686_CMD_WRITE_INPUT_N; + gpiod_set_value_cansleep(st->ldac_gpio, 0); + } else { + cmd = AD5686_CMD_WRITE_INPUT_N_UPDATE_N; + } + + i = 0; + iio_for_each_active_channel(indio_dev, ch) { + if (st->ops->write(st, cmd, indio_dev->channels[ch].address, val[i++])) + break; + } + + /* + * If sync() is available, it is called here regardless of write + * failure to allow bus implementation to reset. In that case, partial + * writes are unlikely as the write operations would just queue up + * the transfers. + */ + if (st->ops->sync) + st->ops->sync(st); + + if (async_update) + gpiod_set_value_cansleep(st->ldac_gpio, 1); +} + +static irqreturn_t ad5686_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + do_ad5686_trigger_handler(indio_dev); + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, - const char *name, const struct ad5686_bus_ops *ops) + const char *name, const struct ad5686_bus_ops *ops, + void *bus_data) { + struct reset_control *rstc; struct ad5686_state *st; struct iio_dev *indio_dev; int ret, i; @@ -482,14 +698,56 @@ int ad5686_probe(struct device *dev, st->dev = dev; st->ops = ops; + st->bus_data = bus_data; st->chip_info = chip_info; - ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); + 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 control\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, "vlogic"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable vlogic supply\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret == -ENODEV) /* vcc-supply is deprecated, but supported still */ + ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); if (ret < 0 && ret != -ENODEV) - return ret; + return dev_err_probe(dev, ret, "failed to read vref voltage\n"); st->use_internal_vref = ret == -ENODEV; st->vref_mv = st->use_internal_vref ? st->chip_info->int_vref_mv : ret / 1000; + if (!st->vref_mv) + return dev_err_probe(dev, -EINVAL, + "invalid or not provided vref voltage\n"); + + /* 4.5us power-up time: Datasheet Table 4: Timing Characteristics */ + fsleep(5); + + /* 1us >> 30ns reset pulse activation time: Datasheet Table 4 */ + reset_control_assert(rstc); + fsleep(1); + reset_control_deassert(rstc); + + st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH); + if (IS_ERR(st->ldac_gpio)) + return dev_err_probe(dev, PTR_ERR(st->ldac_gpio), + "Failed to get LDAC GPIO\n"); + + st->double_scale = device_property_read_bool(dev, "adi,range-double"); + st->gain_gpio = devm_gpiod_get_optional(dev, "gain", + st->double_scale ? GPIOD_OUT_HIGH : + GPIOD_OUT_LOW); + if (IS_ERR(st->gain_gpio)) + return dev_err_probe(dev, PTR_ERR(st->gain_gpio), + "Failed to get GAIN GPIO\n"); + + ad5686_init_scale_avail(st); /* Initialize masks to all ones */ st->pwr_down_mask = ~0; @@ -526,7 +784,7 @@ int ad5686_probe(struct device *dev, break; case AD5686_REGMAP: ret = ad5686_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, - st->use_internal_vref ? 0 : AD5686_REF_BIT_MSK); + st->use_internal_vref ? 0 : AD5686_DATA_REF_MSK); if (ret) return ret; break; @@ -534,6 +792,13 @@ int ad5686_probe(struct device *dev, return -EINVAL; } + ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL, + &ad5686_trigger_handler, + IIO_BUFFER_DIRECTION_OUT, + NULL, NULL); + if (ret) + return ret; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686"); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index a06fe7d89305..482bc70515f7 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -14,15 +14,18 @@ #include <linux/iio/iio.h> -#define AD5310_CMD(x) ((x) << 12) +#define AD5310_CMD_MSK GENMASK(15, 12) +#define AD5310_DATA_MSK GENMASK(11, 0) -#define AD5683_DATA(x) ((x) << 4) +#define AD5683_DATA_MSK GENMASK(19, 4) -#define AD5686_ADDR(x) ((x) << 16) -#define AD5686_CMD(x) ((x) << 20) +#define AD5686_CMD_MSK GENMASK(23, 20) +#define AD5686_ADDR_MSK GENMASK(19, 16) +#define AD5686_DATA_MSK GENMASK(15, 0) #define AD5686_ADDR_DAC(chan) (0x1 << (chan)) #define AD5686_ADDR_ALL_DAC 0xF +#define AD5686_MAX_CHANNELS 16 #define AD5686_CMD_NOOP 0x0 #define AD5686_CMD_WRITE_INPUT_N 0x1 @@ -38,15 +41,17 @@ #define AD5686_CMD_CONTROL_REG 0x4 #define AD5686_CMD_READBACK_ENABLE_V2 0x5 -#define AD5310_REF_BIT_MSK BIT(8) -#define AD5310_PD_MSK GENMASK(10, 9) +#define AD5310_DATA_GAIN_MSK BIT(7) +#define AD5310_DATA_REF_MSK BIT(8) +#define AD5310_DATA_PD_MSK GENMASK(10, 9) -#define AD5683_REF_BIT_MSK BIT(12) -#define AD5683_PD_MSK GENMASK(14, 13) +#define AD5683_DATA_GAIN_MSK BIT(11) /* DB15 */ +#define AD5683_DATA_REF_MSK BIT(12) /* DB16 */ +#define AD5683_DATA_PD_MSK GENMASK(14, 13) /* DB18:DB17 */ -#define AD5686_REF_BIT_MSK BIT(0) -#define AD5686_PD_MSK GENMASK(1, 0) +#define AD5686_DATA_REF_MSK BIT(0) +#define AD5686_PD_MSK GENMASK(1, 0) #define AD5686_PD_MODE_1K_TO_GND 0x1 #define AD5686_PD_MODE_100K_TO_GND 0x2 #define AD5686_PD_MODE_THREE_STATE 0x3 @@ -60,16 +65,20 @@ enum ad5686_regmap_type { AD5686_REGMAP, }; +struct gpio_desc; + struct ad5686_state; /** * struct ad5686_bus_ops - bus specific read/write operations * @read: read a register value at the given address * @write: write a command, address and value to the device + * @sync: ensure the completion of the write operation (optional) */ struct ad5686_bus_ops { int (*read)(struct ad5686_state *st, u8 addr); int (*write)(struct ad5686_state *st, u8 cmd, u8 addr, u16 val); + int (*sync)(struct ad5686_state *st); }; /** @@ -97,8 +106,13 @@ extern const struct ad5686_chip_info ad5683r_chip_info; /* dual-channel instances */ extern const struct ad5686_chip_info ad5337r_chip_info; extern const struct ad5686_chip_info ad5338r_chip_info; +extern const struct ad5686_chip_info ad5687_chip_info; +extern const struct ad5686_chip_info ad5687r_chip_info; +extern const struct ad5686_chip_info ad5689_chip_info; +extern const struct ad5686_chip_info ad5689r_chip_info; /* quad-channel instances */ +extern const struct ad5686_chip_info ad5317r_chip_info; extern const struct ad5686_chip_info ad5684_chip_info; extern const struct ad5686_chip_info ad5684r_chip_info; extern const struct ad5686_chip_info ad5685r_chip_info; @@ -111,7 +125,9 @@ extern const struct ad5686_chip_info ad5676_chip_info; extern const struct ad5686_chip_info ad5676r_chip_info; /* 16-channel instances */ +extern const struct ad5686_chip_info ad5674_chip_info; extern const struct ad5686_chip_info ad5674r_chip_info; +extern const struct ad5686_chip_info ad5679_chip_info; extern const struct ad5686_chip_info ad5679r_chip_info; /** @@ -119,23 +135,33 @@ extern const struct ad5686_chip_info ad5679r_chip_info; * @dev: device instance * @chip_info: chip model specific constants, available modes etc * @ops: bus specific operations - * @vref_mv: actual reference voltage used + * @ldac_gpio: LDAC pin GPIO descriptor + * @gain_gpio: GAIN pin GPIO descriptor * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode + * @scale_avail: pre-calculated available scale values + * @vref_mv: actual reference voltage used + * @double_scale: flag to indicate the gain multiplier is applied * @use_internal_vref: set to true if the internal reference voltage is used * @lock: lock to protect access to state fields, which includes * the data buffer during regmap ops + * @bus_data: bus specific data * @data: transfer buffers */ struct ad5686_state { struct device *dev; const struct ad5686_chip_info *chip_info; const struct ad5686_bus_ops *ops; - unsigned short vref_mv; + struct gpio_desc *ldac_gpio; + struct gpio_desc *gain_gpio; unsigned int pwr_down_mask; unsigned int pwr_down_mode; + int scale_avail[4]; + unsigned short vref_mv; + bool double_scale; bool use_internal_vref; struct mutex lock; + void *bus_data; /* * DMA (thus cache coherency maintenance) may require the @@ -146,17 +172,24 @@ struct ad5686_state { __be32 d32; __be16 d16; u8 d8[4]; - } data[3] __aligned(IIO_DMA_MINALIGN); + } data[AD5686_MAX_CHANNELS] __aligned(IIO_DMA_MINALIGN); }; int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, - const char *name, const struct ad5686_bus_ops *ops); + const char *name, const struct ad5686_bus_ops *ops, + void *bus_data); static inline int ad5686_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val) { - return st->ops->write(st, cmd, addr, val); + int ret; + + ret = st->ops->write(st, cmd, addr, val); + if (ret) + return ret; + + return st->ops->sync ? st->ops->sync(st) : 0; } static inline int ad5686_read(struct ad5686_state *st, u8 addr) diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index d49946adbde3..f91abfcdbada 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, - * AD5693R, AD5694, AD5694R, AD5695R, AD5696, AD5696R - * Digital to analog converters driver + * I2C driver for AD5696 and similar Digital to Analog Converters * * Copyright 2018 Analog Devices Inc. */ +#include <linux/bitfield.h> #include <linux/errno.h> #include <linux/i2c.h> #include <linux/module.h> @@ -34,9 +33,8 @@ static int ad5686_i2c_read(struct ad5686_state *st, u8 addr) }; int ret; - st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP) | - AD5686_ADDR(addr) | - 0x00); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, AD5686_CMD_NOOP) | + FIELD_PREP(AD5686_ADDR_MSK, addr)); ret = i2c_transfer(i2c->adapter, msg, 2); if (ret < 0) @@ -51,8 +49,9 @@ static int ad5686_i2c_write(struct ad5686_state *st, struct i2c_client *i2c = to_i2c_client(st->dev); int ret; - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr) - | val); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr) | + FIELD_PREP(AD5686_DATA_MSK, val)); ret = i2c_master_send(i2c, &st->data[0].d8[1], 3); if (ret < 0) @@ -68,16 +67,23 @@ static const struct ad5686_bus_ops ad5686_i2c_ops = { static int ad5686_i2c_probe(struct i2c_client *i2c) { - return ad5686_probe(&i2c->dev, i2c_get_match_data(i2c), - i2c->name, &ad5686_i2c_ops); + const struct ad5686_chip_info *info; + + info = i2c_get_match_data(i2c); + if (!info) + return -ENODATA; + + return ad5686_probe(&i2c->dev, info, i2c->name, &ad5686_i2c_ops, NULL); } static const struct i2c_device_id ad5686_i2c_id[] = { { .name = "ad5311r", .driver_data = (kernel_ulong_t)&ad5311r_chip_info }, + { .name = "ad5316r", .driver_data = (kernel_ulong_t)&ad5317r_chip_info }, { .name = "ad5337r", .driver_data = (kernel_ulong_t)&ad5337r_chip_info }, { .name = "ad5338r", .driver_data = (kernel_ulong_t)&ad5338r_chip_info }, { .name = "ad5671r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info }, { .name = "ad5673r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info }, + { .name = "ad5675", .driver_data = (kernel_ulong_t)&ad5676_chip_info }, { .name = "ad5675r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info }, { .name = "ad5677r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info }, { .name = "ad5691r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info }, @@ -89,16 +95,21 @@ static const struct i2c_device_id ad5686_i2c_id[] = { { .name = "ad5695r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info }, { .name = "ad5696", .driver_data = (kernel_ulong_t)&ad5686_chip_info }, { .name = "ad5696r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info }, + { .name = "ad5697r", .driver_data = (kernel_ulong_t)&ad5687r_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5311r", .data = &ad5311r_chip_info }, + { .compatible = "adi,ad5316r", .data = &ad5317r_chip_info }, { .compatible = "adi,ad5337r", .data = &ad5337r_chip_info }, { .compatible = "adi,ad5338r", .data = &ad5338r_chip_info }, { .compatible = "adi,ad5671r", .data = &ad5672r_chip_info }, + { .compatible = "adi,ad5673r", .data = &ad5674r_chip_info }, + { .compatible = "adi,ad5675", .data = &ad5676_chip_info }, { .compatible = "adi,ad5675r", .data = &ad5676r_chip_info }, + { .compatible = "adi,ad5677r", .data = &ad5679r_chip_info }, { .compatible = "adi,ad5691r", .data = &ad5681r_chip_info }, { .compatible = "adi,ad5692r", .data = &ad5682r_chip_info }, { .compatible = "adi,ad5693", .data = &ad5683_chip_info }, @@ -108,6 +119,7 @@ static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5695r", .data = &ad5685r_chip_info }, { .compatible = "adi,ad5696", .data = &ad5686_chip_info }, { .compatible = "adi,ad5696r", .data = &ad5686r_chip_info }, + { .compatible = "adi,ad5697r", .data = &ad5687r_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad5686_of_match); diff --git a/drivers/iio/dac/ad5706r.c b/drivers/iio/dac/ad5706r.c index e4e48ca7ad34..06c4860a7eba 100644 --- a/drivers/iio/dac/ad5706r.c +++ b/drivers/iio/dac/ad5706r.c @@ -232,7 +232,7 @@ static const struct of_device_id ad5706r_of_match[] = { MODULE_DEVICE_TABLE(of, ad5706r_of_match); static const struct spi_device_id ad5706r_id[] = { - { "ad5706r" }, + { .name = "ad5706r" }, { } }; MODULE_DEVICE_TABLE(spi, ad5706r_id); diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index cc6d56140d66..0828b9909099 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -849,11 +849,11 @@ static int ad5755_probe(struct spi_device *spi) } static const struct spi_device_id ad5755_id[] = { - { "ad5755", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, - { "ad5755-1", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, - { "ad5757", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] }, - { "ad5735", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] }, - { "ad5737", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] }, + { .name = "ad5755", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, + { .name = "ad5755-1", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, + { .name = "ad5757", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] }, + { .name = "ad5735", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] }, + { .name = "ad5737", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] }, { } }; MODULE_DEVICE_TABLE(spi, ad5755_id); diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index bb30842e7080..44efa373cd80 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -878,7 +878,7 @@ static int ad5758_probe(struct spi_device *spi) } static const struct spi_device_id ad5758_id[] = { - { "ad5758", 0 }, + { .name = "ad5758" }, { } }; MODULE_DEVICE_TABLE(spi, ad5758_id); diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c index b5d20f04f070..c49568b943d6 100644 --- a/drivers/iio/dac/ad5761.c +++ b/drivers/iio/dac/ad5761.c @@ -342,10 +342,10 @@ static int ad5761_probe(struct spi_device *spi) } static const struct spi_device_id ad5761_id[] = { - {"ad5721", ID_AD5721}, - {"ad5721r", ID_AD5721R}, - {"ad5761", ID_AD5761}, - {"ad5761r", ID_AD5761R}, + { .name = "ad5721", .driver_data = ID_AD5721 }, + { .name = "ad5721r", .driver_data = ID_AD5721R }, + { .name = "ad5761", .driver_data = ID_AD5761 }, + { .name = "ad5761r", .driver_data = ID_AD5761R }, { } }; MODULE_DEVICE_TABLE(spi, ad5761_id); diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c index fbbd7105a80c..2be4bee7eb01 100644 --- a/drivers/iio/dac/ad5764.c +++ b/drivers/iio/dac/ad5764.c @@ -342,10 +342,10 @@ static void ad5764_remove(struct spi_device *spi) } static const struct spi_device_id ad5764_ids[] = { - { "ad5744", ID_AD5744 }, - { "ad5744r", ID_AD5744R }, - { "ad5764", ID_AD5764 }, - { "ad5764r", ID_AD5764R }, + { .name = "ad5744", .driver_data = ID_AD5744 }, + { .name = "ad5744r", .driver_data = ID_AD5744R }, + { .name = "ad5764", .driver_data = ID_AD5764 }, + { .name = "ad5764r", .driver_data = ID_AD5764R }, { } }; MODULE_DEVICE_TABLE(spi, ad5764_ids); diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index f6a0a0d84fef..061b43fc0345 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -651,8 +651,8 @@ static const struct of_device_id ad5766_dt_match[] = { MODULE_DEVICE_TABLE(of, ad5766_dt_match); static const struct spi_device_id ad5766_spi_ids[] = { - { "ad5766", ID_AD5766 }, - { "ad5767", ID_AD5767 }, + { .name = "ad5766", .driver_data = ID_AD5766 }, + { .name = "ad5767", .driver_data = ID_AD5767 }, { } }; MODULE_DEVICE_TABLE(spi, ad5766_spi_ids); diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index 6027e8d88b27..bdc48e57013f 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -642,7 +642,7 @@ static const struct of_device_id ad5770r_of_id[] = { MODULE_DEVICE_TABLE(of, ad5770r_of_id); static const struct spi_device_id ad5770r_id[] = { - { "ad5770r", 0 }, + { .name = "ad5770r" }, { } }; MODULE_DEVICE_TABLE(spi, ad5770r_id); diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index ae7297f08398..a5eda135c9bd 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -586,11 +586,11 @@ static const struct of_device_id ad5791_of_match[] = { MODULE_DEVICE_TABLE(of, ad5791_of_match); static const struct spi_device_id ad5791_id[] = { - { "ad5760", (kernel_ulong_t)&ad5760_chip_info }, - { "ad5780", (kernel_ulong_t)&ad5780_chip_info }, - { "ad5781", (kernel_ulong_t)&ad5781_chip_info }, - { "ad5790", (kernel_ulong_t)&ad5790_chip_info }, - { "ad5791", (kernel_ulong_t)&ad5791_chip_info }, + { .name = "ad5760", .driver_data = (kernel_ulong_t)&ad5760_chip_info }, + { .name = "ad5780", .driver_data = (kernel_ulong_t)&ad5780_chip_info }, + { .name = "ad5781", .driver_data = (kernel_ulong_t)&ad5781_chip_info }, + { .name = "ad5790", .driver_data = (kernel_ulong_t)&ad5790_chip_info }, + { .name = "ad5791", .driver_data = (kernel_ulong_t)&ad5791_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5791_id); diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c index 03acf7c114b0..33a49813ece0 100644 --- a/drivers/iio/dac/ad7293.c +++ b/drivers/iio/dac/ad7293.c @@ -869,7 +869,7 @@ static int ad7293_probe(struct spi_device *spi) } static const struct spi_device_id ad7293_id[] = { - { "ad7293", 0 }, + { .name = "ad7293" }, { } }; MODULE_DEVICE_TABLE(spi, ad7293_id); diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index 6451fc586a67..83153e7d7fc8 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -270,7 +270,7 @@ static const struct of_device_id ad7303_spi_of_match[] = { MODULE_DEVICE_TABLE(of, ad7303_spi_of_match); static const struct spi_device_id ad7303_spi_ids[] = { - { "ad7303", 0 }, + { .name = "ad7303" }, { } }; MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); diff --git a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c index ddec62b6e57b..a2292810e4cf 100644 --- a/drivers/iio/dac/ad8460.c +++ b/drivers/iio/dac/ad8460.c @@ -954,3 +954,4 @@ MODULE_AUTHOR("Mariel Tinaco <mariel.tinaco@analog.com"); MODULE_DESCRIPTION("AD8460 DAC driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c index 60e663af1cc1..08d7d74e999e 100644 --- a/drivers/iio/dac/ad8801.c +++ b/drivers/iio/dac/ad8801.c @@ -151,8 +151,8 @@ static int ad8801_probe(struct spi_device *spi) } static const struct spi_device_id ad8801_ids[] = { - {"ad8801", ID_AD8801}, - {"ad8803", ID_AD8803}, + { .name = "ad8801", .driver_data = ID_AD8801 }, + { .name = "ad8803", .driver_data = ID_AD8803 }, { } }; MODULE_DEVICE_TABLE(spi, ad8801_ids); diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c index ccd6a3b1e891..5263a0c968f5 100644 --- a/drivers/iio/dac/ad9739a.c +++ b/drivers/iio/dac/ad9739a.c @@ -446,7 +446,7 @@ static const struct of_device_id ad9739a_of_match[] = { MODULE_DEVICE_TABLE(of, ad9739a_of_match); static const struct spi_device_id ad9739a_id[] = { - {"ad9739a"}, + { .name = "ad9739a" }, { } }; MODULE_DEVICE_TABLE(spi, ad9739a_id); diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index cf6d94e7af84..de36e2e27330 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -253,3 +253,4 @@ module_platform_driver(dpot_dac_driver); MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer"); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c index 6e80b49f4665..90ffb58e7927 100644 --- a/drivers/iio/dac/ltc1660.c +++ b/drivers/iio/dac/ltc1660.c @@ -224,8 +224,8 @@ static const struct of_device_id ltc1660_dt_ids[] = { MODULE_DEVICE_TABLE(of, ltc1660_dt_ids); static const struct spi_device_id ltc1660_id[] = { - {"ltc1660", ID_LTC1660}, - {"ltc1665", ID_LTC1665}, + { .name = "ltc1660", .driver_data = ID_LTC1660 }, + { .name = "ltc1665", .driver_data = ID_LTC1665 }, { } }; MODULE_DEVICE_TABLE(spi, ltc1660_id); diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index d6a3d290e7a8..3800aee8dd6e 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -362,28 +362,28 @@ static int ltc2632_probe(struct spi_device *spi) } static const struct spi_device_id ltc2632_id[] = { - { "ltc2632-l12", (kernel_ulong_t)<c2632l12_chip_info }, - { "ltc2632-l10", (kernel_ulong_t)<c2632l10_chip_info }, - { "ltc2632-l8", (kernel_ulong_t)<c2632l8_chip_info }, - { "ltc2632-h12", (kernel_ulong_t)<c2632h12_chip_info }, - { "ltc2632-h10", (kernel_ulong_t)<c2632h10_chip_info }, - { "ltc2632-h8", (kernel_ulong_t)<c2632h8_chip_info }, - { "ltc2634-l12", (kernel_ulong_t)<c2634l12_chip_info }, - { "ltc2634-l10", (kernel_ulong_t)<c2634l10_chip_info }, - { "ltc2634-l8", (kernel_ulong_t)<c2634l8_chip_info }, - { "ltc2634-h12", (kernel_ulong_t)<c2634h12_chip_info }, - { "ltc2634-h10", (kernel_ulong_t)<c2634h10_chip_info }, - { "ltc2634-h8", (kernel_ulong_t)<c2634h8_chip_info }, - { "ltc2636-l12", (kernel_ulong_t)<c2636l12_chip_info }, - { "ltc2636-l10", (kernel_ulong_t)<c2636l10_chip_info }, - { "ltc2636-l8", (kernel_ulong_t)<c2636l8_chip_info }, - { "ltc2636-h12", (kernel_ulong_t)<c2636h12_chip_info }, - { "ltc2636-h10", (kernel_ulong_t)<c2636h10_chip_info }, - { "ltc2636-h8", (kernel_ulong_t)<c2636h8_chip_info }, - { "ltc2654-l16", (kernel_ulong_t)<c2654l16_chip_info }, - { "ltc2654-l12", (kernel_ulong_t)<c2634l12_chip_info }, - { "ltc2654-h16", (kernel_ulong_t)<c2654h16_chip_info }, - { "ltc2654-h12", (kernel_ulong_t)<c2634h12_chip_info }, + { .name = "ltc2632-l12", .driver_data = (kernel_ulong_t)<c2632l12_chip_info }, + { .name = "ltc2632-l10", .driver_data = (kernel_ulong_t)<c2632l10_chip_info }, + { .name = "ltc2632-l8", .driver_data = (kernel_ulong_t)<c2632l8_chip_info }, + { .name = "ltc2632-h12", .driver_data = (kernel_ulong_t)<c2632h12_chip_info }, + { .name = "ltc2632-h10", .driver_data = (kernel_ulong_t)<c2632h10_chip_info }, + { .name = "ltc2632-h8", .driver_data = (kernel_ulong_t)<c2632h8_chip_info }, + { .name = "ltc2634-l12", .driver_data = (kernel_ulong_t)<c2634l12_chip_info }, + { .name = "ltc2634-l10", .driver_data = (kernel_ulong_t)<c2634l10_chip_info }, + { .name = "ltc2634-l8", .driver_data = (kernel_ulong_t)<c2634l8_chip_info }, + { .name = "ltc2634-h12", .driver_data = (kernel_ulong_t)<c2634h12_chip_info }, + { .name = "ltc2634-h10", .driver_data = (kernel_ulong_t)<c2634h10_chip_info }, + { .name = "ltc2634-h8", .driver_data = (kernel_ulong_t)<c2634h8_chip_info }, + { .name = "ltc2636-l12", .driver_data = (kernel_ulong_t)<c2636l12_chip_info }, + { .name = "ltc2636-l10", .driver_data = (kernel_ulong_t)<c2636l10_chip_info }, + { .name = "ltc2636-l8", .driver_data = (kernel_ulong_t)<c2636l8_chip_info }, + { .name = "ltc2636-h12", .driver_data = (kernel_ulong_t)<c2636h12_chip_info }, + { .name = "ltc2636-h10", .driver_data = (kernel_ulong_t)<c2636h10_chip_info }, + { .name = "ltc2636-h8", .driver_data = (kernel_ulong_t)<c2636h8_chip_info }, + { .name = "ltc2654-l16", .driver_data = (kernel_ulong_t)<c2654l16_chip_info }, + { .name = "ltc2654-l12", .driver_data = (kernel_ulong_t)<c2634l12_chip_info }, + { .name = "ltc2654-h16", .driver_data = (kernel_ulong_t)<c2654h16_chip_info }, + { .name = "ltc2654-h12", .driver_data = (kernel_ulong_t)<c2634h12_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ltc2632_id); diff --git a/drivers/iio/dac/ltc2664.c b/drivers/iio/dac/ltc2664.c index c48b9efc8280..35bec55ebde1 100644 --- a/drivers/iio/dac/ltc2664.c +++ b/drivers/iio/dac/ltc2664.c @@ -708,8 +708,8 @@ static int ltc2664_probe(struct spi_device *spi) } static const struct spi_device_id ltc2664_id[] = { - { "ltc2664", (kernel_ulong_t)<c2664_chip }, - { "ltc2672", (kernel_ulong_t)<c2672_chip }, + { .name = "ltc2664", .driver_data = (kernel_ulong_t)<c2664_chip }, + { .name = "ltc2672", .driver_data = (kernel_ulong_t)<c2672_chip }, { } }; MODULE_DEVICE_TABLE(spi, ltc2664_id); diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c index a575ef8371dc..0301bb7dce31 100644 --- a/drivers/iio/dac/ltc2688.c +++ b/drivers/iio/dac/ltc2688.c @@ -991,7 +991,7 @@ static const struct of_device_id ltc2688_of_id[] = { MODULE_DEVICE_TABLE(of, ltc2688_of_id); static const struct spi_device_id ltc2688_id[] = { - { "ltc2688" }, + { .name = "ltc2688" }, { } }; MODULE_DEVICE_TABLE(spi, ltc2688_id); diff --git a/drivers/iio/dac/max22007.c b/drivers/iio/dac/max22007.c index d747901df5a3..e2c7e2ad37ba 100644 --- a/drivers/iio/dac/max22007.c +++ b/drivers/iio/dac/max22007.c @@ -464,7 +464,7 @@ static int max22007_probe(struct spi_device *spi) } static const struct spi_device_id max22007_id[] = { - { "max22007" }, + { .name = "max22007" }, { } }; MODULE_DEVICE_TABLE(spi, max22007_id); diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c index 1459ba132df6..f3415748c765 100644 --- a/drivers/iio/dac/max5522.c +++ b/drivers/iio/dac/max5522.c @@ -24,15 +24,8 @@ #define MAX5522_REG_DATA(x) ((x) + MAX5522_CTRL_LOAD_IN_A) -struct max5522_chip_info { - const char *name; - const struct iio_chan_spec *channels; - unsigned int num_channels; -}; - struct max5522_state { struct regmap *regmap; - const struct max5522_chip_info *chip_info; unsigned short dac_cache[2]; int vref_mV; }; @@ -57,18 +50,6 @@ static const struct iio_chan_spec max5522_channels[] = { MAX5522_CHANNEL(1), }; -enum max5522_type { - ID_MAX5522, -}; - -static const struct max5522_chip_info max5522_chip_info_tbl[] = { - [ID_MAX5522] = { - .name = "max5522", - .channels = max5522_channels, - .num_channels = 2, - }, -}; - static inline int max5522_info_to_reg(struct iio_chan_spec const *chan) { return MAX5522_REG_DATA(chan->channel); @@ -139,9 +120,6 @@ static int max5522_spi_probe(struct spi_device *spi) } state = iio_priv(indio_dev); - state->chip_info = spi_get_device_match_data(spi); - if (!state->chip_info) - return -EINVAL; ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefin"); if (ret < 0) @@ -158,22 +136,19 @@ static int max5522_spi_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = max5522_channels; indio_dev->num_channels = ARRAY_SIZE(max5522_channels); - indio_dev->name = max5522_chip_info_tbl[ID_MAX5522].name; + indio_dev->name = "max5522"; return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id max5522_ids[] = { - { "max5522", (kernel_ulong_t)&max5522_chip_info_tbl[ID_MAX5522] }, + { .name = "max5522" }, { } }; MODULE_DEVICE_TABLE(spi, max5522_ids); static const struct of_device_id max5522_of_match[] = { - { - .compatible = "maxim,max5522", - .data = &max5522_chip_info_tbl[ID_MAX5522], - }, + { .compatible = "maxim,max5522" }, { } }; MODULE_DEVICE_TABLE(of, max5522_of_match); diff --git a/drivers/iio/dac/mcf54415_dac.c b/drivers/iio/dac/mcf54415_dac.c new file mode 100644 index 000000000000..e2c12241a534 --- /dev/null +++ b/drivers/iio/dac/mcf54415_dac.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP mcf54415 DAC driver + * + * Copyright 2026 BayLibre - adureghello@baylibre.com + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include <linux/iio/iio.h> + +#define MCF54415_DAC_CR 0x00 +#define MCF54415_DAC_CR_PDN BIT(0) +#define MCF54415_DAC_CR_HSLS BIT(6) +#define MCF54415_DAC_CR_WMLVL GENMASK(9, 8) +#define MCF54415_DAC_CR_FILT BIT(12) + +#define MCF54415_DAC_DATA 0x02 + +struct mcf54415_dac { + struct regmap *map; + struct clk *clk; +}; + +static const struct regmap_config mcf54415_dac_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x0c, /* DACX_FILTCNT, R.M. Table 30-2 */ + .val_format_endian = REGMAP_ENDIAN_BIG, + .reg_format_endian = REGMAP_ENDIAN_BIG, +}; + +static int mcf54415_dac_init(struct mcf54415_dac *info) +{ + u16 val = MCF54415_DAC_CR_FILT | FIELD_PREP(MCF54415_DAC_CR_WMLVL, 1); + int ret; + + /* Fixed defaults and enable DAC (bit 0 set to 0) */ + ret = regmap_write(info->map, MCF54415_DAC_CR, val); + if (ret) + return ret; + + /* DAC is ready after 12us, from RM table 40-3 */ + fsleep(12); + + return 0; +} + +static void mcf54415_dac_exit(void *data) +{ + struct mcf54415_dac *info = data; + + regmap_set_bits(info->map, MCF54415_DAC_CR, MCF54415_DAC_CR_PDN); +} + +static const struct iio_chan_spec mcf54415_dac_iio_channel = { + .type = IIO_VOLTAGE, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + +static int mcf54415_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcf54415_dac *info = iio_priv(indio_dev); + unsigned int reg; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(info->map, MCF54415_DAC_DATA, ®); + if (ret) + return ret; + *val = reg & GENMASK(11, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* Reference voltage as per ColdFire datasheet is 3.3V */ + *val = 3300 /* mV */; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int mcf54415_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mcf54415_dac *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* Check based on RM 30.3.2 (DACn_DATA) reg. resolution */ + if (val < 0 || val > 4095) + return -EINVAL; + return regmap_write(info->map, MCF54415_DAC_DATA, val); + default: + return -EINVAL; + } +} + +static const struct iio_info mcf54415_dac_iio_info = { + .read_raw = &mcf54415_read_raw, + .write_raw = &mcf54415_write_raw, +}; + +static int mcf54415_dac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct mcf54415_dac *info; + void __iomem *regs; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return dev_err_probe(dev, PTR_ERR(regs), "failed to get io regs\n"); + + info->map = devm_regmap_init_mmio(dev, regs, &mcf54415_dac_regmap_config); + if (IS_ERR(info->map)) + return PTR_ERR(info->map); + + info->clk = devm_clk_get_enabled(dev, "dac"); + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n"); + + indio_dev->name = "mcf54415"; + indio_dev->info = &mcf54415_dac_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &mcf54415_dac_iio_channel; + indio_dev->num_channels = 1; + + ret = mcf54415_dac_init(info); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, mcf54415_dac_exit, info); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct platform_device_id mcf54415_dac_ids[] = { + { .name = "mcfdac" }, + { } +}; +MODULE_DEVICE_TABLE(platform, mcf54415_dac_ids); + +static struct platform_driver mcf54415_dac_driver = { + .driver = { + .name = "mcf54415_dac", + }, + .probe = mcf54415_dac_probe, + .id_table = mcf54415_dac_ids, +}; +module_platform_driver(mcf54415_dac_driver); + +MODULE_AUTHOR("Angelo Dureghello <angelo@kernel-space.org>"); +MODULE_DESCRIPTION("NXP MCF54415 DAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/mcp47a1.c b/drivers/iio/dac/mcp47a1.c new file mode 100644 index 000000000000..0bf994aa0e4f --- /dev/null +++ b/drivers/iio/dac/mcp47a1.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Microchip MCP47A1 DAC driver + * + * Copyright (c) 2026 Joshua Crofts <joshua.crofts1@gmail.com> + * + * Datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/25154A.pdf + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/iio/iio.h> + +#define MCP47A1_CMD_CODE 0x00 +#define MCP47A1_MAX_STEPS 64 + +struct mcp47a1_data { + struct i2c_client *client; + int vref_mV; +}; + +static const int mcp47a1_raw_avail[] = { 0, 1, MCP47A1_MAX_STEPS - 1 }; + +static const struct iio_chan_spec mcp47a1_channel = { + .type = IIO_VOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + +static int mcp47a1_write(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mcp47a1_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!in_range(val, 0, MCP47A1_MAX_STEPS)) + return -EINVAL; + + return i2c_smbus_write_byte_data(data->client, MCP47A1_CMD_CODE, + val); + default: + return -EINVAL; + } +} + +static int mcp47a1_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcp47a1_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_smbus_read_byte_data(data->client, MCP47A1_CMD_CODE); + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = data->vref_mV; + *val2 = MCP47A1_MAX_STEPS; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int mcp47a1_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_RAW: + *vals = mcp47a1_raw_avail; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static const struct iio_info mcp47a1_info = { + .write_raw = mcp47a1_write, + .read_raw = mcp47a1_read, + .read_avail = mcp47a1_read_avail, +}; + +static int mcp47a1_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mcp47a1_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulator\n"); + + /* Delay after device exits reset state (see AC/DC characteristics) */ + fsleep(20); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read vref\n"); + + data->vref_mV = ret / (MICRO / MILLI); + + indio_dev->name = "mcp47a1"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp47a1_info; + indio_dev->channels = &mcp47a1_channel; + indio_dev->num_channels = 1; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id mcp47a1_of_match[] = { + { .compatible = "microchip,mcp47a1" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp47a1_of_match); + +static const struct i2c_device_id mcp47a1_id[] = { + { .name = "mcp47a1" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp47a1_id); + +static struct i2c_driver mcp47a1_driver = { + .driver = { + .name = "mcp47a1", + .of_match_table = mcp47a1_of_match, + }, + .probe = mcp47a1_probe, + .id_table = mcp47a1_id, +}; +module_i2c_driver(mcp47a1_driver); + +MODULE_AUTHOR("Joshua Crofts <joshua.crofts1@gmail.com>"); +MODULE_DESCRIPTION("Microchip MCP47A1 DAC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c index d2e7c930c848..8d8c00cd60e9 100644 --- a/drivers/iio/dac/mcp4821.c +++ b/drivers/iio/dac/mcp4821.c @@ -297,12 +297,12 @@ static const struct of_device_id mcp4821_of_table[] = { MODULE_DEVICE_TABLE(of, mcp4821_of_table); static const struct spi_device_id mcp4821_id_table[] = { - { "mcp4801", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801]}, - { "mcp4802", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802]}, - { "mcp4811", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811]}, - { "mcp4812", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812]}, - { "mcp4821", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821]}, - { "mcp4822", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822]}, + { .name = "mcp4801", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801] }, + { .name = "mcp4802", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802] }, + { .name = "mcp4811", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811] }, + { .name = "mcp4812", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812] }, + { .name = "mcp4821", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821] }, + { .name = "mcp4822", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822] }, { } }; MODULE_DEVICE_TABLE(spi, mcp4821_id_table); diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c index 74f338afcab9..15072a9081ff 100644 --- a/drivers/iio/dac/mcp4922.c +++ b/drivers/iio/dac/mcp4922.c @@ -157,10 +157,10 @@ static int mcp4922_probe(struct spi_device *spi) static const struct spi_device_id mcp4922_id[] = { - {"mcp4902", ID_MCP4902}, - {"mcp4912", ID_MCP4912}, - {"mcp4921", ID_MCP4921}, - {"mcp4922", ID_MCP4922}, + { .name = "mcp4902", .driver_data = ID_MCP4902 }, + { .name = "mcp4912", .driver_data = ID_MCP4912 }, + { .name = "mcp4921", .driver_data = ID_MCP4921 }, + { .name = "mcp4922", .driver_data = ID_MCP4922 }, { } }; MODULE_DEVICE_TABLE(spi, mcp4922_id); diff --git a/drivers/iio/dac/rohm-bd79703.c b/drivers/iio/dac/rohm-bd79703.c index 68e66e7286d8..127ccc2c4a22 100644 --- a/drivers/iio/dac/rohm-bd79703.c +++ b/drivers/iio/dac/rohm-bd79703.c @@ -217,10 +217,10 @@ static int bd79703_probe(struct spi_device *spi) } static const struct spi_device_id bd79703_id[] = { - { "bd79700", (kernel_ulong_t)&bd79700_chip_data }, - { "bd79701", (kernel_ulong_t)&bd79701_chip_data }, - { "bd79702", (kernel_ulong_t)&bd79702_chip_data }, - { "bd79703", (kernel_ulong_t)&bd79703_chip_data }, + { .name = "bd79700", .driver_data = (kernel_ulong_t)&bd79700_chip_data }, + { .name = "bd79701", .driver_data = (kernel_ulong_t)&bd79701_chip_data }, + { .name = "bd79702", .driver_data = (kernel_ulong_t)&bd79702_chip_data }, + { .name = "bd79703", .driver_data = (kernel_ulong_t)&bd79703_chip_data }, { } }; MODULE_DEVICE_TABLE(spi, bd79703_id); diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c index 6e62c1f302c8..afa7d9bad5de 100644 --- a/drivers/iio/dac/ti-dac082s085.c +++ b/drivers/iio/dac/ti-dac082s085.c @@ -334,12 +334,12 @@ static const struct of_device_id ti_dac_of_id[] = { MODULE_DEVICE_TABLE(of, ti_dac_of_id); static const struct spi_device_id ti_dac_spi_id[] = { - { "dac082s085", dual_8bit }, - { "dac102s085", dual_10bit }, - { "dac122s085", dual_12bit }, - { "dac084s085", quad_8bit }, - { "dac104s085", quad_10bit }, - { "dac124s085", quad_12bit }, + { .name = "dac082s085", .driver_data = dual_8bit }, + { .name = "dac102s085", .driver_data = dual_10bit }, + { .name = "dac122s085", .driver_data = dual_12bit }, + { .name = "dac084s085", .driver_data = quad_8bit }, + { .name = "dac104s085", .driver_data = quad_10bit }, + { .name = "dac124s085", .driver_data = quad_12bit }, { } }; MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 5c1c5213962f..e5843bd35259 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -311,9 +311,9 @@ static const struct of_device_id ti_dac_of_id[] = { MODULE_DEVICE_TABLE(of, ti_dac_of_id); static const struct spi_device_id ti_dac_spi_id[] = { - { "dac5311", ID_DAC5311 }, - { "dac6311", ID_DAC6311 }, - { "dac7311", ID_DAC7311 }, + { .name = "dac5311", .driver_data = ID_DAC5311 }, + { .name = "dac6311", .driver_data = ID_DAC6311 }, + { .name = "dac7311", .driver_data = ID_DAC7311 }, { } }; MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); diff --git a/drivers/iio/dac/ti-dac7612.c b/drivers/iio/dac/ti-dac7612.c index c308eca02b88..9065663cd609 100644 --- a/drivers/iio/dac/ti-dac7612.c +++ b/drivers/iio/dac/ti-dac7612.c @@ -165,7 +165,7 @@ static int dac7612_probe(struct spi_device *spi) } static const struct spi_device_id dac7612_id[] = { - {"ti-dac7612"}, + { .name = "ti-dac7612" }, { } }; MODULE_DEVICE_TABLE(spi, dac7612_id); diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c index 8bcd298e1f3d..b88689e60cc8 100644 --- a/drivers/iio/filter/admv8818.c +++ b/drivers/iio/filter/admv8818.c @@ -789,7 +789,7 @@ static int admv8818_probe(struct spi_device *spi) } static const struct spi_device_id admv8818_id[] = { - { "admv8818", 0 }, + { .name = "admv8818" }, { } }; MODULE_DEVICE_TABLE(spi, admv8818_id); diff --git a/drivers/iio/flow/Kconfig b/drivers/iio/flow/Kconfig new file mode 100644 index 000000000000..e0e1a8e3654a --- /dev/null +++ b/drivers/iio/flow/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Liquid / gas flow sensor drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Flow sensors" + +config SENSIRION_SLF3S + tristate "Sensirion SLF3S liquid flow sensor" + depends on I2C + select CRC8 + help + Say yes here to build support for the Sensirion SLF3S family of + digital liquid-flow sensors (SLF3S-0600F, SLF3S-1300F and + SLF3S-4000B). The driver reports the volumetric flow rate and the + embedded temperature reading via the standard IIO interface. + + To compile this driver as a module, choose M here: the module + will be called slf3s. + +endmenu diff --git a/drivers/iio/flow/Makefile b/drivers/iio/flow/Makefile new file mode 100644 index 000000000000..3cf4ab95c69c --- /dev/null +++ b/drivers/iio/flow/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for industrial I/O flow sensor drivers +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_SENSIRION_SLF3S) += slf3s.o diff --git a/drivers/iio/flow/slf3s.c b/drivers/iio/flow/slf3s.c new file mode 100644 index 000000000000..dfa7c1409045 --- /dev/null +++ b/drivers/iio/flow/slf3s.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SLF3S liquid flow sensor driver. + * + * Supports the SLF3S-0600F, SLF3S-1300F and SLF3S-4000B liquid-flow + * sensors over I2C. Each measurement frame returns a 16-bit signed + * flow value, a 16-bit signed temperature value and a status word, + * each protected by a CRC-8 byte. + * + * The active calibration medium (water or isopropyl alcohol) is + * runtime-switchable via the in_volumeflow_medium sysfs attribute and + * defaults to water. + * + * Datasheet: https://sensirion.com/products/catalog/SLF3S-0600F/ + * + * Copyright (C) 2026 CMBlu Energy GmbH + * Author: Wadim Mueller <wafgo01@gmail.com> + */ + +#include <linux/array_size.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/math.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/unaligned.h> +#include <linux/units.h> + +#include <linux/iio/iio.h> + +#define SLF3S_CRC8_POLY 0x31 +#define SLF3S_CRC8_INIT 0xff + +#define SLF3S_PRODUCT_ID_LEN 18 +#define SLF3S_PRODUCT_FAMILY_BYTE 1 +#define SLF3S_PRODUCT_SUBTYPE_BYTE 3 +#define SLF3S_PRODUCT_FAMILY_ID 0x03 + +/* Datasheet section 2.2: tPU = 25 ms max from power-on to first cmd. */ +#define SLF3S_POWER_UP_DELAY_US (25 * USEC_PER_MSEC) +/* Datasheet section 2.2: tw = 60 ms typical until first valid sample. */ +#define SLF3S_MEAS_START_DELAY_US (60 * USEC_PER_MSEC) + +static const u8 slf3s_cmd_prep_pid[] = { 0x36, 0x7c }; +static const u8 slf3s_cmd_read_pid[] = { 0xe1, 0x02 }; +static const u8 slf3s_cmd_start_water[] = { 0x36, 0x08 }; +static const u8 slf3s_cmd_start_ipa[] = { 0x36, 0x15 }; +static const u8 slf3s_cmd_stop_meas[] = { 0x3f, 0xf9 }; + +enum slf3s_medium { + SLF3S_MEDIUM_WATER, + SLF3S_MEDIUM_IPA, +}; + +static const char * const slf3s_medium_modes[] = { + [SLF3S_MEDIUM_WATER] = "water", + [SLF3S_MEDIUM_IPA] = "ipa", +}; + +enum slf3s_variant_id { + SLF3S_0600F, + SLF3S_1300F, + SLF3S_4000B, +}; + +/** + * struct slf3s_variant - per-variant calibration constants + * @sub_type: product-info sub-type byte returned by the sensor + * @name: name reported via @iio_dev.name + * @scale: flow scale in l/s per LSB + */ +struct slf3s_variant { + u8 sub_type; + const char *name; + struct s32_fract scale; +}; + +static const struct slf3s_variant slf3s_variants[] = { + [SLF3S_0600F] = { + .sub_type = 0x03, + .name = "slf3s-0600f", + .scale = { .numerator = 1, .denominator = 600 * MICRO }, + }, + [SLF3S_1300F] = { + .sub_type = 0x02, + .name = "slf3s-1300f", + .scale = { .numerator = 1, .denominator = 30 * MICRO }, + }, + [SLF3S_4000B] = { + .sub_type = 0x05, + .name = "slf3s-4000b", + .scale = { .numerator = 1, .denominator = 1920 * MILLI }, + }, +}; + +/** + * struct slf3s_data - per-device state + * @client: I2C client this instance is bound to + * @vdd: supply regulator, disabled while suspended + * @variant: pointer into @slf3s_variants for the detected device + * @medium: currently active calibration medium + * @lock: serialises the multi-step command/response exchanges + * @crc_table: pre-computed CRC-8 lookup table for SLF3S_CRC8_POLY + */ +struct slf3s_data { + struct i2c_client *client; + struct regulator *vdd; + const struct slf3s_variant *variant; + enum slf3s_medium medium; + struct mutex lock; + u8 crc_table[CRC8_TABLE_SIZE]; +}; + +static int slf3s_send_cmd(struct i2c_client *client, const u8 *cmd) +{ + int ret; + + ret = i2c_master_send(client, cmd, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + return 0; +} + +/* Start continuous measurement and wait until the first sample is valid. */ +static int slf3s_start_meas(struct slf3s_data *sf, enum slf3s_medium medium) +{ + const u8 *cmd = (medium == SLF3S_MEDIUM_IPA) ? slf3s_cmd_start_ipa + : slf3s_cmd_start_water; + int ret; + + ret = slf3s_send_cmd(sf->client, cmd); + if (ret) + return ret; + + fsleep(SLF3S_MEAS_START_DELAY_US); + + return 0; +} + +static bool slf3s_crc_valid(const struct slf3s_data *sf, const u8 *block) +{ + return crc8(sf->crc_table, block, 2, SLF3S_CRC8_INIT) == block[2]; +} + +/* + * Read the product-info block and pick the matching variant. The + * sub-type byte returned by the sensor is the source of truth; a + * DT-supplied compatible only seeds an initial guess and is overridden + * on mismatch (with an informational message so misconfigured device + * trees are easy to spot). + * + * Bus / CRC failures are real errors and fail probe. An unknown + * sub-type byte falls back to the variant named in the device tree / + * I2C table, so a drop-in replacement part that lists one of the known + * compatibles keeps working on an older kernel that does not know its + * sub-type yet. Without any match data probe fails since no + * meaningful scale can be published. + */ +static int slf3s_detect_variant(struct slf3s_data *sf) +{ + struct i2c_client *client = sf->client; + u8 buf[SLF3S_PRODUCT_ID_LEN]; + int ret; + + ret = slf3s_send_cmd(client, slf3s_cmd_prep_pid); + if (ret) + return ret; + + ret = slf3s_send_cmd(client, slf3s_cmd_read_pid); + if (ret) + return ret; + + ret = i2c_master_recv(client, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + for (unsigned int i = 0; i < SLF3S_PRODUCT_ID_LEN; i += 3) { + if (!slf3s_crc_valid(sf, &buf[i])) + return -EIO; + } + + if (buf[SLF3S_PRODUCT_FAMILY_BYTE] != SLF3S_PRODUCT_FAMILY_ID) + dev_info(&client->dev, + "unexpected family byte 0x%02x (expected 0x%02x)\n", + buf[SLF3S_PRODUCT_FAMILY_BYTE], + SLF3S_PRODUCT_FAMILY_ID); + + for (unsigned int i = 0; i < ARRAY_SIZE(slf3s_variants); i++) { + if (buf[SLF3S_PRODUCT_SUBTYPE_BYTE] != + slf3s_variants[i].sub_type) + continue; + + if (sf->variant && sf->variant != &slf3s_variants[i]) + dev_info(&client->dev, + "DT compatible says %s but sensor reports %s; using the latter\n", + sf->variant->name, + slf3s_variants[i].name); + + sf->variant = &slf3s_variants[i]; + + return 0; + } + + if (sf->variant) { + dev_warn(&client->dev, + "unknown SLF3S sub-type 0x%02x, assuming %s\n", + buf[SLF3S_PRODUCT_SUBTYPE_BYTE], sf->variant->name); + return 0; + } + + dev_err(&client->dev, "unknown SLF3S sub-type 0x%02x\n", + buf[SLF3S_PRODUCT_SUBTYPE_BYTE]); + + return -ENODEV; +} + +static int slf3s_read_sample(struct slf3s_data *sf, int *flow, int *temp) +{ + /* + * A measurement frame is flow, temperature and a signaling-flags + * word, each followed by a CRC byte. Only flow and temperature are + * used, so the read is stopped after their two words (6 bytes). + */ + u8 buf[6]; + int ret; + + ret = i2c_master_recv(sf->client, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + for (unsigned int i = 0; i < sizeof(buf); i += 3) { + if (!slf3s_crc_valid(sf, &buf[i])) + return -EIO; + } + + *flow = sign_extend32(get_unaligned_be16(&buf[0]), 15); + *temp = sign_extend32(get_unaligned_be16(&buf[3]), 15); + + return 0; +} + +static int slf3s_get_medium(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct slf3s_data *sf = iio_priv(indio_dev); + + return sf->medium; +} + +static int slf3s_set_medium(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct slf3s_data *sf = iio_priv(indio_dev); + int ret; + + guard(mutex)(&sf->lock); + + ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas); + if (ret) + return ret; + + ret = slf3s_start_meas(sf, mode); + if (ret) { + /* + * Try to restart with the previous medium so the sensor is + * not left idle, which would fail all subsequent reads. + */ + if (slf3s_start_meas(sf, sf->medium)) + dev_warn(&sf->client->dev, + "failed to restart measurement, reads will fail until a medium is set\n"); + return ret; + } + + sf->medium = mode; + + return 0; +} + +static const struct iio_enum slf3s_medium_enum = { + .items = slf3s_medium_modes, + .num_items = ARRAY_SIZE(slf3s_medium_modes), + .get = slf3s_get_medium, + .set = slf3s_set_medium, +}; + +static const struct iio_chan_spec_ext_info slf3s_ext_info[] = { + IIO_ENUM("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum), + IIO_ENUM_AVAILABLE("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum), + { } +}; + +static const struct iio_chan_spec slf3s_channels[] = { + { + .type = IIO_VOLUMEFLOW, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .ext_info = slf3s_ext_info, + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int slf3s_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct slf3s_data *sf = iio_priv(indio_dev); + int flow, temp, ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + scoped_guard(mutex, &sf->lock) + ret = slf3s_read_sample(sf, &flow, &temp); + if (ret) + return ret; + + *val = (chan->type == IIO_VOLUMEFLOW) ? flow : temp; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_VOLUMEFLOW) { + /* + * The variant scale is the flow per LSB in l/s, but + * IIO reports volume flow in m^3/s (1 l = 1e-3 m^3). + * These values are tiny (~1.67e-12 m^3/s for the + * SLF3S-0600F), so emit a 64-bit fixed-point value with + * femto (1e-15) resolution to preserve precision. + * Converting l/s to m^3/s (/ MILLI) and scaling to femto + * (* FEMTO) leaves a net * (FEMTO / MILLI) factor. + */ + const struct slf3s_variant *v = sf->variant; + s64 num = (s64)v->scale.numerator * (FEMTO / MILLI); + s64 scale = DIV_S64_ROUND_CLOSEST(num, + v->scale.denominator); + + iio_val_s64_decompose(scale, val, val2); + + return IIO_VAL_DECIMAL64_FEMTO; + } + /* Temperature LSB = 1/200 degC; IIO_TEMP wants milli-degC. */ + *val = MILLIDEGREE_PER_DEGREE / 200; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info slf3s_info = { + .read_raw = slf3s_read_raw, +}; + +static void slf3s_stop_meas(void *data) +{ + struct slf3s_data *sf = data; + + slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas); +} + +static void slf3s_disable_vdd(void *data) +{ + struct slf3s_data *sf = data; + + regulator_disable(sf->vdd); +} + +static int slf3s_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct slf3s_data *sf; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*sf)); + if (!indio_dev) + return -ENOMEM; + + sf = iio_priv(indio_dev); + sf->client = client; + i2c_set_clientdata(client, indio_dev); + sf->variant = i2c_get_match_data(client); + sf->medium = SLF3S_MEDIUM_WATER; + crc8_populate_msb(sf->crc_table, SLF3S_CRC8_POLY); + + ret = devm_mutex_init(dev, &sf->lock); + if (ret) + return ret; + + sf->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(sf->vdd)) + return dev_err_probe(dev, PTR_ERR(sf->vdd), + "failed to get vdd supply\n"); + + ret = regulator_enable(sf->vdd); + if (ret) + return dev_err_probe(dev, ret, "failed to enable vdd supply\n"); + + ret = devm_add_action_or_reset(dev, slf3s_disable_vdd, sf); + if (ret) + return ret; + + fsleep(SLF3S_POWER_UP_DELAY_US); + + /* + * The sensor may still be in continuous measurement mode from a + * previous boot (warm reboot / kexec); in that case it would NACK + * the product-id command below. Stop it first and ignore the error + * if it was already idle. + */ + slf3s_send_cmd(client, slf3s_cmd_stop_meas); + + ret = slf3s_detect_variant(sf); + if (ret) + return dev_err_probe(dev, ret, "product info read failed\n"); + + ret = slf3s_start_meas(sf, sf->medium); + if (ret) + return dev_err_probe(dev, ret, + "failed to start measurement\n"); + + ret = devm_add_action_or_reset(dev, slf3s_stop_meas, sf); + if (ret) + return ret; + + indio_dev->name = sf->variant->name; + indio_dev->channels = slf3s_channels; + indio_dev->num_channels = ARRAY_SIZE(slf3s_channels); + indio_dev->info = &slf3s_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(dev, indio_dev); +} + +/* + * The sensor has no low-power state of its own, so stop the measurement + * and cut the supply while suspended. Resume powers it back up, waits + * out the power-up time and restarts with the medium that was active + * before. + */ +static int slf3s_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct slf3s_data *sf = iio_priv(indio_dev); + int ret; + + guard(mutex)(&sf->lock); + + ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas); + if (ret) + return ret; + + return regulator_disable(sf->vdd); +} + +static int slf3s_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct slf3s_data *sf = iio_priv(indio_dev); + int ret; + + guard(mutex)(&sf->lock); + + ret = regulator_enable(sf->vdd); + if (ret) + return ret; + + fsleep(SLF3S_POWER_UP_DELAY_US); + + return slf3s_start_meas(sf, sf->medium); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(slf3s_pm_ops, slf3s_suspend, slf3s_resume); + +static const struct i2c_device_id slf3s_id[] = { + { + .name = "slf3s-0600f", + .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_0600F], + }, + { + .name = "slf3s-1300f", + .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_1300F], + }, + { + .name = "slf3s-4000b", + .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_4000B], + }, + { } +}; +MODULE_DEVICE_TABLE(i2c, slf3s_id); + +static const struct of_device_id slf3s_of_match[] = { + { + .compatible = "sensirion,slf3s-0600f", + .data = &slf3s_variants[SLF3S_0600F], + }, + { + .compatible = "sensirion,slf3s-1300f", + .data = &slf3s_variants[SLF3S_1300F], + }, + { + .compatible = "sensirion,slf3s-4000b", + .data = &slf3s_variants[SLF3S_4000B], + }, + { } +}; +MODULE_DEVICE_TABLE(of, slf3s_of_match); + +static struct i2c_driver slf3s_driver = { + .driver = { + .name = "slf3s", + .of_match_table = slf3s_of_match, + .pm = pm_sleep_ptr(&slf3s_pm_ops), + }, + .probe = slf3s_probe, + .id_table = slf3s_id, +}; +module_i2c_driver(slf3s_driver); + +MODULE_AUTHOR("Wadim Mueller <wafgo01@gmail.com>"); +MODULE_DESCRIPTION("Sensirion SLF3S liquid flow sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 583cbdf4e8cd..90c6304c4bcd 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -29,6 +29,16 @@ endmenu menu "Phase-Locked Loop (PLL) frequency synthesizers" +config ADF41513 + tristate "Analog Devices ADF41513 PLL Frequency Synthesizer" + depends on SPI + help + Say yes here to build support for Analog Devices ADF41513 + 26.5 GHz Integer-N/Fractional-N PLL Frequency Synthesizer. + + To compile this driver as a module, choose M here: the + module will be called adf41513. + config ADF4350 tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers" depends on SPI diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 70d0e0b70e80..53b4d01414d8 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD9523) += ad9523.o +obj-$(CONFIG_ADF41513) += adf41513.o obj-$(CONFIG_ADF4350) += adf4350.o obj-$(CONFIG_ADF4371) += adf4371.o obj-$(CONFIG_ADF4377) += adf4377.o diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index ea4d2763564a..1e393f40a8c8 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -990,7 +990,6 @@ static int ad9523_probe(struct spi_device *spi) if (IS_ERR(st->sync_gpio)) return PTR_ERR(st->sync_gpio); - spi_set_drvdata(spi, indio_dev); st->spi = spi; st->pdata = pdata; @@ -1009,7 +1008,7 @@ static int ad9523_probe(struct spi_device *spi) } static const struct spi_device_id ad9523_id[] = { - {"ad9523-1", 9523}, + { .name = "ad9523-1" }, { } }; MODULE_DEVICE_TABLE(spi, ad9523_id); diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41513.c new file mode 100644 index 000000000000..c497027012ba --- /dev/null +++ b/drivers/iio/frequency/adf41513.c @@ -0,0 +1,1246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADF41513 SPI PLL Frequency Synthesizer driver + * + * Copyright 2026 Analog Devices Inc. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/kstrtox.h> +#include <linux/log2.h> +#include <linux/math64.h> +#include <linux/minmax.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> +#include <linux/time64.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/iio/iio.h> + +/* Registers */ +#define ADF41513_REG0 0 +#define ADF41513_REG1 1 +#define ADF41513_REG2 2 +#define ADF41513_REG3 3 +#define ADF41513_REG4 4 +#define ADF41513_REG5 5 +#define ADF41513_REG6 6 +#define ADF41513_REG7 7 +#define ADF41513_REG8 8 +#define ADF41513_REG9 9 +#define ADF41513_REG10 10 +#define ADF41513_REG11 11 +#define ADF41513_REG12 12 +#define ADF41513_REG13 13 +#define ADF41513_REG_NUM 14 + +#define ADF41513_SYNC_REG0 BIT(ADF41513_REG0) +#define ADF41513_SYNC_REG1 BIT(ADF41513_REG1) +#define ADF41513_SYNC_REG2 BIT(ADF41513_REG2) +#define ADF41513_SYNC_REG3 BIT(ADF41513_REG3) +#define ADF41513_SYNC_REG4 BIT(ADF41513_REG4) +#define ADF41513_SYNC_REG5 BIT(ADF41513_REG5) +#define ADF41513_SYNC_REG6 BIT(ADF41513_REG6) +#define ADF41513_SYNC_REG7 BIT(ADF41513_REG7) +#define ADF41513_SYNC_REG9 BIT(ADF41513_REG9) +#define ADF41513_SYNC_REG11 BIT(ADF41513_REG11) +#define ADF41513_SYNC_REG12 BIT(ADF41513_REG12) +#define ADF41513_SYNC_REG13 BIT(ADF41513_REG13) +#define ADF41513_SYNC_DIFF 0 +#define ADF41513_SYNC_ALL GENMASK(ADF41513_REG13, ADF41513_REG0) + +/* REG0 Bit Definitions */ +#define ADF41513_REG0_CTRL_BITS_MSK GENMASK(3, 0) +#define ADF41513_REG0_INT_MSK GENMASK(19, 4) +#define ADF41513_REG0_VAR_MOD_MSK BIT(28) + +/* REG1 Bit Definitions */ +#define ADF41513_REG1_FRAC1_MSK GENMASK(28, 4) +#define ADF41513_REG1_DITHER2_MSK BIT(31) + +/* REG2 Bit Definitions */ +#define ADF41513_REG2_PHASE_VAL_MSK GENMASK(15, 4) +#define ADF41513_REG2_PHASE_ADJ_MSK BIT(31) + +/* REG3 Bit Definitions */ +#define ADF41513_REG3_FRAC2_MSK GENMASK(27, 4) + +/* REG4 Bit Definitions */ +#define ADF41513_REG4_MOD2_MSK GENMASK(27, 4) + +/* REG5 Bit Definitions */ +#define ADF41513_REG5_CLK1_DIV_MSK GENMASK(15, 4) +#define ADF41513_REG5_R_CNT_MSK GENMASK(20, 16) +#define ADF41513_REG5_REF_DOUBLER_MSK BIT(21) +#define ADF41513_REG5_RDIV2_MSK BIT(22) +#define ADF41513_REG5_PRESCALER_MSK BIT(23) +#define ADF41513_REG5_LSB_P1_MSK BIT(24) +#define ADF41513_REG5_CP_CURRENT_MSK GENMASK(28, 25) +#define ADF41513_REG5_DLD_MODES_MSK GENMASK(31, 30) + +/* REG6 Bit Definitions */ +#define ADF41513_REG6_COUNTER_RESET_MSK BIT(4) +#define ADF41513_REG6_CP_TRISTATE_MSK BIT(5) +#define ADF41513_REG6_POWER_DOWN_MSK BIT(6) +#define ADF41513_REG6_PD_POLARITY_MSK BIT(7) +#define ADF41513_REG6_LDP_MSK GENMASK(9, 8) +#define ADF41513_REG6_CP_TRISTATE_PD_ON_MSK BIT(16) +#define ADF41513_REG6_SD_RESET_MSK BIT(17) +#define ADF41513_REG6_LOL_ENABLE_MSK BIT(18) +#define ADF41513_REG6_ABP_MSK BIT(19) +#define ADF41513_REG6_INT_MODE_MSK BIT(20) +#define ADF41513_REG6_BLEED_ENABLE_MSK BIT(22) +#define ADF41513_REG6_BLEED_POLARITY_MSK BIT(23) +#define ADF41513_REG6_BLEED_CURRENT_MSK GENMASK(31, 24) + +/* REG7 Bit Definitions */ +#define ADF41513_REG7_CLK2_DIV_MSK GENMASK(17, 6) +#define ADF41513_REG7_CLK_DIV_MODE_MSK GENMASK(19, 18) +#define ADF41513_REG7_PS_BIAS_MSK GENMASK(21, 20) +#define ADF41513_REG7_N_DELAY_MSK GENMASK(23, 22) +#define ADF41513_REG7_LD_CLK_SEL_MSK BIT(26) +#define ADF41513_REG7_LD_COUNT_MSK GENMASK(29, 27) + +/* REG9 Bit Definitions */ +#define ADF41513_REG9_LD_BIAS_MSK GENMASK(31, 30) + +/* REG11 Bit Definitions */ +#define ADF41513_REG11_POWER_DOWN_SEL_MSK BIT(31) + +/* REG12 Bit Definitions */ +#define ADF41513_REG12_READBACK_SEL_MSK GENMASK(19, 14) +#define ADF41513_REG12_LE_SELECT_MSK BIT(20) +#define ADF41513_REG12_MASTER_RESET_MSK BIT(22) +#define ADF41513_REG12_LOGIC_LEVEL_MSK BIT(27) +#define ADF41513_REG12_MUXOUT_MSK GENMASK(31, 28) + +/* MUXOUT Selection */ +#define ADF41513_MUXOUT_TRISTATE 0x0 +#define ADF41513_MUXOUT_DVDD 0x1 +#define ADF41513_MUXOUT_DGND 0x2 +#define ADF41513_MUXOUT_R_DIV 0x3 +#define ADF41513_MUXOUT_N_DIV 0x4 +#define ADF41513_MUXOUT_DIG_LD 0x6 +#define ADF41513_MUXOUT_SDO 0x7 +#define ADF41513_MUXOUT_READBACK 0x8 +#define ADF41513_MUXOUT_CLK1_DIV 0xA +#define ADF41513_MUXOUT_R_DIV2 0xD +#define ADF41513_MUXOUT_N_DIV2 0xE + +/* DLD Mode Selection */ +#define ADF41513_DLD_TRISTATE 0x0 +#define ADF41513_DLD_DIG_LD 0x1 +#define ADF41513_DLD_LOW 0x2 +#define ADF41513_DLD_HIGH 0x3 + +/* Prescaler Selection */ +#define ADF41513_PRESCALER_4_5 0 +#define ADF41513_PRESCALER_8_9 1 +#define ADF41513_PRESCALER_AUTO 2 + +/* CLK Divider mode */ +#define ADF41513_CLK_DIV_MODE_OFF 0 +#define ADF41513_CLK_DIV_MODE_PHASE_RESYNC 2 + +/* Specifications */ +#define ADF41510_MAX_RF_FREQ_HZ (10ULL * HZ_PER_GHZ) +#define ADF41513_MIN_RF_FREQ_HZ (1ULL * HZ_PER_GHZ) +#define ADF41513_MAX_RF_FREQ_HZ (26500ULL * HZ_PER_MHZ) + +#define ADF41513_MIN_REF_FREQ_HZ (10 * HZ_PER_MHZ) +#define ADF41513_MAX_REF_FREQ_HZ (800 * HZ_PER_MHZ) +#define ADF41513_MAX_REF_FREQ_DOUBLER_HZ (225 * HZ_PER_MHZ) + +#define ADF41513_MAX_PFD_FREQ_INT_N_UHZ (250ULL * MEGA * MICROHZ_PER_HZ) +#define ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ (125ULL * MEGA * MICROHZ_PER_HZ) +#define ADF41513_MAX_FREQ_RESOLUTION_UHZ (100ULL * KILO * MICROHZ_PER_HZ) + +#define ADF41513_MIN_INT_4_5 20 +#define ADF41513_MAX_INT_4_5 511 +#define ADF41513_MIN_INT_8_9 64 +#define ADF41513_MAX_INT_8_9 1023 + +#define ADF41513_MIN_INT_FRAC_4_5 23 +#define ADF41513_MIN_INT_FRAC_8_9 75 + +#define ADF41513_MIN_R_CNT 1 +#define ADF41513_MAX_R_CNT 32 + +#define ADF41513_MIN_R_SET 1800 +#define ADF41513_DEFAULT_R_SET 2700 +#define ADF41513_MAX_R_SET 10000 + +#define ADF41513_MIN_CP_VOLTAGE_mV 810 +#define ADF41513_DEFAULT_CP_VOLTAGE_mV 6480 +#define ADF41513_MAX_CP_VOLTAGE_mV 12960 + +#define ADF41513_MIN_CP_CURRENT_uA 81 +#define ADF41513_MAX_CP_CURRENT_uA 7200 + +#define ADF41513_LD_COUNT_FAST_MIN 2 +#define ADF41513_LD_COUNT_FAST_LIMIT 64 +#define ADF41513_LD_COUNT_MIN 64 +#define ADF41513_LD_COUNT_MAX 8192 + +#define ADF41513_FIXED_MODULUS BIT(25) +#define ADF41513_MAX_MOD2 (BIT(24) - 1) +#define ADF41513_MAX_PHASE_VAL (BIT(12) - 1) +#define ADF41513_MAX_CLK_DIVIDER (BIT(12) - 1) + +#define ADF41513_HZ_DECIMAL_SCALE 6 +#define ADF41513_PS_BIAS_INIT 0x2 +#define ADF41513_MAX_PHASE_MICRORAD ((2 * 314159265UL) / 100) + +enum adf41513_pll_mode { + ADF41513_MODE_INVALID, + ADF41513_MODE_INTEGER_N, + ADF41513_MODE_FIXED_MODULUS, + ADF41513_MODE_VARIABLE_MODULUS, +}; + +struct adf41513_chip_info { + const char *name; + u64 max_rf_freq_hz; + bool has_prescaler_8_9; +}; + +struct adf41513_data { + u64 power_up_frequency_hz; + u64 freq_resolution_uhz; + u32 phase_resync_period_ns; + u32 charge_pump_voltage_mv; + u32 lock_detect_count; + + u8 ref_div_factor; + bool ref_doubler_en; + bool ref_div2_en; + bool phase_detector_polarity; + + bool logic_lvl_1v8_en; + bool le_sync_en; +}; + +struct adf41513_pll_settings { + enum adf41513_pll_mode mode; + + /* reference path parameters */ + u8 r_counter; + u8 ref_doubler; + u8 ref_div2; + u8 prescaler; + + /* frequency parameters */ + u64 target_frequency_uhz; + u64 actual_frequency_uhz; + u64 pfd_frequency_uhz; + + /* pll parameters */ + u32 frac1; + u32 frac2; + u32 mod2; + u16 int_val; +}; + +struct adf41513_state { + const struct adf41513_chip_info *chip_info; + struct spi_device *spi; + struct gpio_desc *lock_detect; + struct clk *ref_clk; + u32 ref_freq_hz; + + /* + * Lock for accessing device registers. Some operations require + * multiple consecutive R/W operations, during which the device + * shouldn't be interrupted. The buffers are also shared across + * all operations so need to be protected on stand alone reads and + * writes. + */ + struct mutex lock; + + /* Cached register values */ + u32 regs[ADF41513_REG_NUM]; + u32 regs_hw[ADF41513_REG_NUM]; + + struct adf41513_data data; + struct adf41513_pll_settings settings; + + bool powerdown; +}; + +static const u16 adf41513_ld_window_x10_ns[] = { + 9, 12, 16, 17, 21, 28, 29, 35, /* 0 - 7 */ + 43, 47, 49, 52, 70, 79, 115, /* 8 - 14 */ +}; + +static const u8 adf41513_ldp_bias[] = { + 0xC, 0xD, 0xE, 0x8, 0x9, 0x4, 0xA, 0x5, /* 0 - 7 */ + 0x0, 0x6, 0xB, 0x1, 0x2, 0x7, 0x3, /* 8 - 14 */ +}; + +static const char * const adf41513_power_supplies[] = { + "avdd1", "avdd2", "avdd3", "avdd4", "avdd5", "vp", +}; + +static int adf41513_sync_config(struct adf41513_state *st, u16 sync_mask) +{ + __be32 d32; + int ret; + + /* write registers in reverse order (R13 to R0)*/ + for (int i = ADF41513_REG13; i >= ADF41513_REG0; i--) { + if (st->regs_hw[i] == st->regs[i] && !(sync_mask & BIT(i))) + continue; + + d32 = cpu_to_be32(st->regs[i] | i); + ret = spi_write_then_read(st->spi, &d32, sizeof(d32), NULL, 0); + if (ret < 0) + return ret; + st->regs_hw[i] = st->regs[i]; + dev_dbg(&st->spi->dev, "REG%d <= 0x%08X\n", i, st->regs[i] | i); + } + + return 0; +} + +static u64 adf41513_pll_get_rate(struct adf41513_state *st) +{ + struct adf41513_pll_settings *cfg = &st->settings; + + if (cfg->mode != ADF41513_MODE_INVALID) + return cfg->actual_frequency_uhz; + + /* get pll settings from regs_hw */ + cfg->int_val = FIELD_GET(ADF41513_REG0_INT_MSK, st->regs_hw[ADF41513_REG0]); + cfg->frac1 = FIELD_GET(ADF41513_REG1_FRAC1_MSK, st->regs_hw[ADF41513_REG1]); + cfg->frac2 = FIELD_GET(ADF41513_REG3_FRAC2_MSK, st->regs_hw[ADF41513_REG3]); + cfg->mod2 = FIELD_GET(ADF41513_REG4_MOD2_MSK, st->regs_hw[ADF41513_REG4]); + cfg->r_counter = FIELD_GET(ADF41513_REG5_R_CNT_MSK, st->regs_hw[ADF41513_REG5]); + cfg->ref_doubler = FIELD_GET(ADF41513_REG5_REF_DOUBLER_MSK, st->regs_hw[ADF41513_REG5]); + cfg->ref_div2 = FIELD_GET(ADF41513_REG5_RDIV2_MSK, st->regs_hw[ADF41513_REG5]); + cfg->prescaler = FIELD_GET(ADF41513_REG5_PRESCALER_MSK, st->regs_hw[ADF41513_REG5]); + + if (!cfg->mod2) + cfg->mod2 = 1; + if (!cfg->r_counter) + cfg->r_counter = ADF41513_MAX_R_CNT; + + /* calculate pfd frequency */ + cfg->pfd_frequency_uhz = (u64)st->ref_freq_hz * MICRO; + if (cfg->ref_doubler) + cfg->pfd_frequency_uhz <<= 1; + if (cfg->ref_div2) + cfg->pfd_frequency_uhz >>= 1; + cfg->pfd_frequency_uhz = div_u64(cfg->pfd_frequency_uhz, cfg->r_counter); + cfg->actual_frequency_uhz = (u64)cfg->int_val * cfg->pfd_frequency_uhz; + + /* check if int mode is selected */ + if (FIELD_GET(ADF41513_REG6_INT_MODE_MSK, st->regs_hw[ADF41513_REG6])) { + cfg->mode = ADF41513_MODE_INTEGER_N; + } else { + cfg->actual_frequency_uhz += mul_u64_u32_div(cfg->pfd_frequency_uhz, + cfg->frac1, + ADF41513_FIXED_MODULUS); + + /* check if variable modulus is selected */ + if (FIELD_GET(ADF41513_REG0_VAR_MOD_MSK, st->regs_hw[ADF41513_REG0])) { + cfg->actual_frequency_uhz += + mul_u64_u64_div_u64(cfg->frac2, + cfg->pfd_frequency_uhz, + (u64)cfg->mod2 * ADF41513_FIXED_MODULUS); + + cfg->mode = ADF41513_MODE_VARIABLE_MODULUS; + } else { + /* LSB_P1 offset */ + if (!FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs_hw[ADF41513_REG5])) + cfg->actual_frequency_uhz += + div_u64(cfg->pfd_frequency_uhz, + 2 * ADF41513_FIXED_MODULUS); + cfg->mode = ADF41513_MODE_FIXED_MODULUS; + } + } + + cfg->target_frequency_uhz = cfg->actual_frequency_uhz; + + return cfg->actual_frequency_uhz; +} + +static int adf41513_calc_pfd_frequency(struct adf41513_state *st, + struct adf41513_pll_settings *result, + u64 fpfd_limit_uhz) +{ + result->ref_div2 = st->data.ref_div2_en; + result->ref_doubler = st->data.ref_doubler_en; + result->r_counter = st->data.ref_div_factor - 1; + + do { + result->r_counter++; + /* f_PFD = REF_IN × ((1 + D)/(R × (1 + T))) */ + result->pfd_frequency_uhz = (u64)st->ref_freq_hz * MICRO; + if (result->ref_doubler) + result->pfd_frequency_uhz <<= 1; + if (result->ref_div2) + result->pfd_frequency_uhz >>= 1; + result->pfd_frequency_uhz = div_u64(result->pfd_frequency_uhz, + result->r_counter); + } while (result->pfd_frequency_uhz > fpfd_limit_uhz); + + if (result->r_counter > ADF41513_MAX_R_CNT) { + dev_err(&st->spi->dev, "Cannot optimize PFD frequency\n"); + return -ERANGE; + } + + return 0; +} + +static int adf41513_calc_integer_n(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u32 max_int = st->chip_info->has_prescaler_8_9 ? + ADF41513_MAX_INT_8_9 : ADF41513_MAX_INT_4_5; + u64 freq_error_uhz; + u32 int_val = div64_u64_rem(result->target_frequency_uhz, result->pfd_frequency_uhz, + &freq_error_uhz); + + /* check if freq error is within a tolerance of 1/2 resolution */ + if (freq_error_uhz > (result->pfd_frequency_uhz >> 1) && int_val < max_int) { + int_val++; + freq_error_uhz = result->pfd_frequency_uhz - freq_error_uhz; + } + + if (freq_error_uhz > st->data.freq_resolution_uhz) + return -ERANGE; + + /* set prescaler */ + if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_8_9 && + int_val <= ADF41513_MAX_INT_8_9) + result->prescaler = 1; + else if (int_val >= ADF41513_MIN_INT_4_5 && int_val <= ADF41513_MAX_INT_4_5) + result->prescaler = 0; + else + return -ERANGE; + + result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz; + result->mode = ADF41513_MODE_INTEGER_N; + result->int_val = int_val; + result->frac1 = 0; + result->frac2 = 0; + result->mod2 = 0; + + return 0; +} + +static int adf41513_calc_fixed_mod(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u64 resolution_uhz = div_u64(result->pfd_frequency_uhz, ADF41513_FIXED_MODULUS); + u64 target_frequency_uhz = result->target_frequency_uhz; + u64 freq_error_uhz; + u32 int_val, frac1; + bool lsb_p1_offset = !FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs[ADF41513_REG5]); + + /* LSB_P1 adds a frequency offset of f_pfd/2^26 */ + if (lsb_p1_offset) + target_frequency_uhz -= resolution_uhz >> 1; + + int_val = div64_u64_rem(target_frequency_uhz, result->pfd_frequency_uhz, + &freq_error_uhz); + + if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_FRAC_8_9 && + int_val <= ADF41513_MAX_INT_8_9) + result->prescaler = 1; + else if (int_val >= ADF41513_MIN_INT_FRAC_4_5 && int_val <= ADF41513_MAX_INT_4_5) + result->prescaler = 0; + else + return -ERANGE; + + /* compute frac1 and fixed modulus error */ + frac1 = mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz); + freq_error_uhz -= mul_u64_u32_div(result->pfd_frequency_uhz, frac1, + ADF41513_FIXED_MODULUS); + + /* check if freq error is within a tolerance of 1/2 resolution */ + if (freq_error_uhz > (resolution_uhz >> 1) && frac1 < (ADF41513_FIXED_MODULUS - 1)) { + frac1++; + freq_error_uhz = freq_error_uhz < resolution_uhz ? + resolution_uhz - freq_error_uhz : 0; + } + + if (freq_error_uhz > st->data.freq_resolution_uhz) + return -ERANGE; + + /* integer part */ + result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz; + /* fractional part */ + if (lsb_p1_offset) + result->actual_frequency_uhz += (resolution_uhz >> 1); + result->actual_frequency_uhz += mul_u64_u32_div(result->pfd_frequency_uhz, frac1, + ADF41513_FIXED_MODULUS); + result->mode = ADF41513_MODE_FIXED_MODULUS; + result->int_val = int_val; + result->frac1 = frac1; + result->frac2 = 0; + result->mod2 = 0; + + return 0; +} + +static int adf41513_calc_variable_mod(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u64 freq_error_uhz, mod2; + u32 frac1, frac2; + u32 int_val = div64_u64_rem(result->target_frequency_uhz, + result->pfd_frequency_uhz, &freq_error_uhz); + + if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_FRAC_8_9 && + int_val <= ADF41513_MAX_INT_8_9) + result->prescaler = 1; + else if (int_val >= ADF41513_MIN_INT_FRAC_4_5 && int_val <= ADF41513_MAX_INT_4_5) + result->prescaler = 0; + else + return -ERANGE; + + /* calculate required mod2 based on target resolution / 2 */ + mod2 = DIV64_U64_ROUND_CLOSEST(result->pfd_frequency_uhz << 1, + st->data.freq_resolution_uhz * ADF41513_FIXED_MODULUS); + /* ensure mod2 is at least 2 for meaningful operation */ + mod2 = clamp(mod2, 2, ADF41513_MAX_MOD2); + + /* calculate frac1 and frac2 */ + frac1 = mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz); + frac2 = mul_u64_u64_div_u64(freq_error_uhz, mod2 * ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz) - mod2 * frac1; + + /* integer part */ + result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz; + /* fractional part */ + result->actual_frequency_uhz += mul_u64_u64_div_u64(mod2 * frac1 + frac2, + result->pfd_frequency_uhz, + mod2 * ADF41513_FIXED_MODULUS); + result->mode = ADF41513_MODE_VARIABLE_MODULUS; + result->int_val = int_val; + result->frac1 = frac1; + result->frac2 = frac2; + result->mod2 = mod2; + + return 0; +} + +static int adf41513_calc_pll_settings(struct adf41513_state *st, + struct adf41513_pll_settings *result, + u64 rf_out_uhz) +{ + u64 max_rf_freq_uhz = st->chip_info->max_rf_freq_hz * MICRO; + u64 min_rf_freq_uhz = ADF41513_MIN_RF_FREQ_HZ * MICRO; + u64 pfd_freq_limit_uhz; + int ret; + + if (rf_out_uhz < min_rf_freq_uhz || rf_out_uhz > max_rf_freq_uhz) { + dev_err(&st->spi->dev, "RF frequency %llu uHz out of range [%llu, %llu] uHz\n", + rf_out_uhz, min_rf_freq_uhz, max_rf_freq_uhz); + return -EINVAL; + } + + result->target_frequency_uhz = rf_out_uhz; + + /* try integer-N first (best phase noise performance) */ + pfd_freq_limit_uhz = min(div_u64(rf_out_uhz, ADF41513_MIN_INT_4_5), + ADF41513_MAX_PFD_FREQ_INT_N_UHZ); + ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz); + if (ret) + return ret; + + if (adf41513_calc_integer_n(st, result) == 0) + return 0; + + /* try fractional-N: recompute pfd frequency if necessary */ + pfd_freq_limit_uhz = min(div_u64(rf_out_uhz, ADF41513_MIN_INT_FRAC_4_5), + ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ); + if (pfd_freq_limit_uhz < result->pfd_frequency_uhz) { + ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz); + if (ret) + return ret; + } + + /* fixed-modulus attempt */ + if (adf41513_calc_fixed_mod(st, result) == 0) + return 0; + + /* variable-modulus attempt */ + ret = adf41513_calc_variable_mod(st, result); + if (ret) { + dev_err(&st->spi->dev, + "no valid PLL configuration found for %llu uHz\n", + rf_out_uhz); + return ret; + } + + return 0; +} + +static void adf41513_set_bleed_val(struct adf41513_state *st) +{ + u32 bleed_value, cp_index; + + if (!(st->regs[ADF41513_REG6] & ADF41513_REG6_BLEED_ENABLE_MSK)) + return; + + if (st->data.phase_detector_polarity) + bleed_value = 90; + else + bleed_value = 144; + + cp_index = 1 + FIELD_GET(ADF41513_REG5_CP_CURRENT_MSK, + st->regs[ADF41513_REG5]); + bleed_value = div64_u64(st->settings.pfd_frequency_uhz * cp_index * bleed_value, + 1600ULL * MEGA * MICROHZ_PER_HZ); + + FIELD_MODIFY(ADF41513_REG6_BLEED_CURRENT_MSK, &st->regs[ADF41513_REG6], + bleed_value); +} + +static void adf41513_set_ld_window(struct adf41513_state *st) +{ + /* + * The ideal lock detector window size is halfway between the max + * window, set by the phase comparison period t_PFD = (1 / f_PFD), + * and the minimum is set by (I_BLEED/I_CP) × t_PFD + */ + u16 ld_window_10x_ns = div64_u64(10ULL * NSEC_PER_SEC * MICROHZ_PER_HZ, + st->settings.pfd_frequency_uhz << 1); + u8 ld_idx, ldp, ld_bias; + + if (st->settings.mode != ADF41513_MODE_INTEGER_N) { + /* account for bleed current (deduced from eq.6 and eq.7) */ + if (st->data.phase_detector_polarity) + ld_window_10x_ns += 4; + else + ld_window_10x_ns += 6; + } + + ld_idx = find_closest(ld_window_10x_ns, adf41513_ld_window_x10_ns, + ARRAY_SIZE(adf41513_ld_window_x10_ns)); + ldp = (adf41513_ldp_bias[ld_idx] >> 2) & 0x3; + ld_bias = adf41513_ldp_bias[ld_idx] & 0x3; + + FIELD_MODIFY(ADF41513_REG6_LDP_MSK, &st->regs[ADF41513_REG6], ldp); + FIELD_MODIFY(ADF41513_REG9_LD_BIAS_MSK, &st->regs[ADF41513_REG9], ld_bias); +} + +static void adf41513_set_phase_resync(struct adf41513_state *st) +{ + u32 total_div, clk1_div, clk2_div; + + if (!st->data.phase_resync_period_ns) + return; + + /* assuming both clock dividers hold similar values */ + total_div = mul_u64_u64_div_u64(st->settings.pfd_frequency_uhz, + st->data.phase_resync_period_ns, + 1ULL * MICROHZ_PER_HZ * NSEC_PER_SEC); + clk1_div = clamp(int_sqrt(total_div), 1, + ADF41513_MAX_CLK_DIVIDER); + clk2_div = clamp(DIV_ROUND_CLOSEST(total_div, clk1_div), 1, + ADF41513_MAX_CLK_DIVIDER); + + FIELD_MODIFY(ADF41513_REG5_CLK1_DIV_MSK, &st->regs[ADF41513_REG5], + clk1_div); + FIELD_MODIFY(ADF41513_REG7_CLK2_DIV_MSK, &st->regs[ADF41513_REG7], + clk2_div); + + /* enable phase resync */ + FIELD_MODIFY(ADF41513_REG7_CLK_DIV_MODE_MSK, &st->regs[ADF41513_REG7], + ADF41513_CLK_DIV_MODE_PHASE_RESYNC); +} + +static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz, u16 sync_mask) +{ + struct adf41513_pll_settings result; + bool pfd_change = false; + bool mode_change = false; + int ret; + + ret = adf41513_calc_pll_settings(st, &result, freq_uhz); + if (ret < 0) + return ret; + + /* apply computed results to pll settings */ + pfd_change = st->settings.pfd_frequency_uhz != result.pfd_frequency_uhz; + mode_change = st->settings.mode != result.mode; + st->settings = result; + + dev_dbg(&st->spi->dev, + "%s mode: int=%u, frac1=%u, frac2=%u, mod2=%u, fpdf=%llu Hz, prescaler=%s\n", + (result.mode == ADF41513_MODE_INTEGER_N) ? "integer-n" : + (result.mode == ADF41513_MODE_FIXED_MODULUS) ? "fixed-modulus" : "variable-modulus", + result.int_val, result.frac1, result.frac2, result.mod2, + div64_u64(result.pfd_frequency_uhz, MICRO), + result.prescaler ? "8/9" : "4/5"); + + st->regs[ADF41513_REG0] = FIELD_PREP(ADF41513_REG0_INT_MSK, + st->settings.int_val); + if (st->settings.mode == ADF41513_MODE_VARIABLE_MODULUS) + st->regs[ADF41513_REG0] |= ADF41513_REG0_VAR_MOD_MSK; + + st->regs[ADF41513_REG1] = FIELD_PREP(ADF41513_REG1_FRAC1_MSK, + st->settings.frac1); + if (st->settings.mode != ADF41513_MODE_INTEGER_N) + st->regs[ADF41513_REG1] |= ADF41513_REG1_DITHER2_MSK; + + st->regs[ADF41513_REG3] = FIELD_PREP(ADF41513_REG3_FRAC2_MSK, + st->settings.frac2); + FIELD_MODIFY(ADF41513_REG4_MOD2_MSK, &st->regs[ADF41513_REG4], + st->settings.mod2); + FIELD_MODIFY(ADF41513_REG5_R_CNT_MSK, &st->regs[ADF41513_REG5], + st->settings.r_counter % ADF41513_MAX_R_CNT); + FIELD_MODIFY(ADF41513_REG5_REF_DOUBLER_MSK, &st->regs[ADF41513_REG5], + st->settings.ref_doubler); + FIELD_MODIFY(ADF41513_REG5_RDIV2_MSK, &st->regs[ADF41513_REG5], + st->settings.ref_div2); + FIELD_MODIFY(ADF41513_REG5_PRESCALER_MSK, &st->regs[ADF41513_REG5], + st->settings.prescaler); + + if (st->settings.mode == ADF41513_MODE_INTEGER_N) { + st->regs[ADF41513_REG6] |= ADF41513_REG6_INT_MODE_MSK; + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_BLEED_ENABLE_MSK; + } else { + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_INT_MODE_MSK; + st->regs[ADF41513_REG6] |= ADF41513_REG6_BLEED_ENABLE_MSK; + } + + if (pfd_change) + adf41513_set_phase_resync(st); + + if (pfd_change || mode_change) { + adf41513_set_bleed_val(st); + adf41513_set_ld_window(st); + } + + return adf41513_sync_config(st, sync_mask | ADF41513_SYNC_REG0); +} + +static int adf41513_suspend(struct adf41513_state *st) +{ + st->regs[ADF41513_REG6] |= FIELD_PREP(ADF41513_REG6_POWER_DOWN_MSK, 1); + st->regs[ADF41513_REG12] &= ~ADF41513_REG12_LE_SELECT_MSK; + return adf41513_sync_config(st, ADF41513_SYNC_DIFF); +} + +static int adf41513_resume(struct adf41513_state *st) +{ + int ret; + + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_POWER_DOWN_MSK; + st->regs[ADF41513_REG12] &= ~ADF41513_REG12_LE_SELECT_MSK; + ret = adf41513_sync_config(st, ADF41513_SYNC_ALL); + if (ret) + return ret; + + if (st->data.le_sync_en) { + st->regs[ADF41513_REG12] |= ADF41513_REG12_LE_SELECT_MSK; + ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF); + if (ret) + return ret; + } + + return 0; +} + +static ssize_t adf41513_read_resolution(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf41513_state *st = iio_priv(indio_dev); + int vals[2]; + + guard(mutex)(&st->lock); + + iio_val_s64_decompose(st->data.freq_resolution_uhz, &vals[0], &vals[1]); + return iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(vals), vals); +} + +static ssize_t adf41513_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u32 val; + + guard(mutex)(&st->lock); + + val = FIELD_GET(ADF41513_REG6_POWER_DOWN_MSK, st->regs_hw[ADF41513_REG6]); + return sysfs_emit(buf, "%u\n", val); +} + +static ssize_t adf41513_write_resolution(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u64 freq_uhz; + int ret; + + ret = kstrtoudec64(buf, ADF41513_HZ_DECIMAL_SCALE, &freq_uhz); + if (ret) + return ret; + + if (freq_uhz == 0 || freq_uhz > ADF41513_MAX_FREQ_RESOLUTION_UHZ) + return -EINVAL; + + guard(mutex)(&st->lock); + + st->data.freq_resolution_uhz = freq_uhz; + return len; +} + +static ssize_t adf41513_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf41513_state *st = iio_priv(indio_dev); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + if (val) + ret = adf41513_suspend(st); + else + ret = adf41513_resume(st); + if (ret) + return ret; + + st->powerdown = val; + return len; +} + +static const struct iio_chan_spec_ext_info adf41513_ext_info[] = { + { + .name = "frequency_resolution", + .read = adf41513_read_resolution, + .write = adf41513_write_resolution, + .shared = IIO_SEPARATE, + }, + { + .name = "powerdown", + .read = adf41513_read_powerdown, + .write = adf41513_write_powerdown, + .shared = IIO_SEPARATE, + }, + { } +}; + +static const struct iio_chan_spec adf41513_chan = { + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY) | + BIT(IIO_CHAN_INFO_PHASE), + .ext_info = adf41513_ext_info, +}; + +static int adf41513_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u64 tmp64; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_FREQUENCY: + if (st->lock_detect && + !gpiod_get_value_cansleep(st->lock_detect)) { + dev_dbg(&st->spi->dev, "PLL un-locked\n"); + return -EBUSY; + } + tmp64 = adf41513_pll_get_rate(st); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + case IIO_CHAN_INFO_PHASE: + tmp64 = FIELD_GET(ADF41513_REG2_PHASE_VAL_MSK, + st->regs_hw[ADF41513_REG2]); + tmp64 = (tmp64 * ADF41513_MAX_PHASE_MICRORAD) >> 12; + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + default: + return -EINVAL; + } +} + +static int adf41513_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u64 tmp64 = iio_val_s64_compose(val, val2); + u16 phase_val; + int ret; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_FREQUENCY: + return adf41513_set_frequency(st, tmp64, ADF41513_SYNC_DIFF); + case IIO_CHAN_INFO_PHASE: + if (tmp64 >= ADF41513_MAX_PHASE_MICRORAD) + return -EINVAL; + + phase_val = DIV_U64_ROUND_CLOSEST(tmp64 << 12, + ADF41513_MAX_PHASE_MICRORAD); + phase_val = min(phase_val, ADF41513_MAX_PHASE_VAL); + st->regs[ADF41513_REG2] |= ADF41513_REG2_PHASE_ADJ_MSK; + FIELD_MODIFY(ADF41513_REG2_PHASE_VAL_MSK, + &st->regs[ADF41513_REG2], phase_val); + ret = adf41513_sync_config(st, ADF41513_SYNC_REG0); + /* clear phase adjust for the next sync */ + st->regs[ADF41513_REG2] &= ~ADF41513_REG2_PHASE_ADJ_MSK; + return ret; + default: + return -EINVAL; + } +} + +static int adf41513_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_FREQUENCY: + case IIO_CHAN_INFO_PHASE: + return IIO_VAL_DECIMAL64_MICRO; + default: + return -EINVAL; + } +} + +static int adf41513_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct adf41513_state *st = iio_priv(indio_dev); + + if (reg > ADF41513_REG13) + return -EINVAL; + + guard(mutex)(&st->lock); + + if (!readval) { + if (reg <= ADF41513_REG6) + st->settings.mode = ADF41513_MODE_INVALID; + st->regs[reg] = writeval & ~0xF; /* Clear control bits */ + return adf41513_sync_config(st, BIT(reg)); + } + + *readval = st->regs_hw[reg]; + return 0; +} + +static const struct iio_info adf41513_info = { + .read_raw = adf41513_read_raw, + .write_raw = adf41513_write_raw, + .write_raw_get_fmt = adf41513_write_raw_get_fmt, + .debugfs_reg_access = &adf41513_reg_access, +}; + +static int adf41513_parse_fw(struct adf41513_state *st) +{ + struct device *dev = &st->spi->dev; + u32 tmp, cp_resistance, cp_current; + int ret; + + tmp = ADF41510_MAX_RF_FREQ_HZ / MEGA; + device_property_read_u32(dev, "adi,power-up-frequency-mhz", &tmp); + st->data.power_up_frequency_hz = (u64)tmp * MEGA; + if (st->data.power_up_frequency_hz < ADF41513_MIN_RF_FREQ_HZ || + st->data.power_up_frequency_hz > st->chip_info->max_rf_freq_hz) + return dev_err_probe(dev, -ERANGE, + "power-up frequency %llu Hz out of range\n", + st->data.power_up_frequency_hz); + + tmp = ADF41513_MIN_R_CNT; + device_property_read_u32(dev, "adi,reference-div-factor", &tmp); + if (tmp < ADF41513_MIN_R_CNT || tmp > ADF41513_MAX_R_CNT) + return dev_err_probe(dev, -ERANGE, + "invalid reference div factor %u\n", tmp); + st->data.ref_div_factor = tmp; + + st->data.ref_div2_en = device_property_read_bool(dev, "adi,reference-div2-enable"); + st->data.ref_doubler_en = device_property_read_bool(dev, "adi,reference-doubler-enable"); + + if (st->data.ref_doubler_en && + st->ref_freq_hz > ADF41513_MAX_REF_FREQ_DOUBLER_HZ) { + return dev_err_probe(dev, -ERANGE, + "Ref frequency not supported with doubler enabled\n"); + } + + cp_resistance = ADF41513_DEFAULT_R_SET; + device_property_read_u32(dev, "adi,charge-pump-resistor-ohms", &cp_resistance); + if (cp_resistance < ADF41513_MIN_R_SET || cp_resistance > ADF41513_MAX_R_SET) + return dev_err_probe(dev, -ERANGE, "R_SET %u Ohms out of range\n", cp_resistance); + + st->data.charge_pump_voltage_mv = ADF41513_DEFAULT_CP_VOLTAGE_mV; + ret = device_property_read_u32(dev, "adi,charge-pump-current-microamp", &cp_current); + if (!ret) { + if (cp_current < ADF41513_MIN_CP_CURRENT_uA || + cp_current > ADF41513_MAX_CP_CURRENT_uA) + return dev_err_probe(dev, -ERANGE, + "I_CP %u uA out of range\n", cp_current); + + tmp = DIV_ROUND_CLOSEST(cp_current * cp_resistance, MILLI); + if (tmp < ADF41513_MIN_CP_VOLTAGE_mV || tmp > ADF41513_MAX_CP_VOLTAGE_mV) + return dev_err_probe(dev, -ERANGE, "I_CP %u uA (%u Ohms) out of range\n", + cp_current, cp_resistance); + st->data.charge_pump_voltage_mv = tmp; + } + + st->data.phase_detector_polarity = + device_property_read_bool(dev, "adi,phase-detector-polarity-positive-enable"); + + st->data.phase_resync_period_ns = 0; + ret = device_property_read_u32(dev, "adi,phase-resync-period-ns", &tmp); + if (!ret) + st->data.phase_resync_period_ns = tmp; + + st->data.logic_lvl_1v8_en = device_property_read_bool(dev, "adi,logic-level-1v8-enable"); + + tmp = ADF41513_LD_COUNT_MIN; + device_property_read_u32(dev, "adi,lock-detector-count", &tmp); + if (tmp < ADF41513_LD_COUNT_FAST_MIN || tmp > ADF41513_LD_COUNT_MAX || + !is_power_of_2(tmp)) + return dev_err_probe(dev, -ERANGE, + "invalid lock detect count: %u\n", tmp); + st->data.lock_detect_count = tmp; + + /* load enable sync */ + st->data.le_sync_en = device_property_read_bool(dev, "adi,le-sync-enable"); + st->data.freq_resolution_uhz = MICROHZ_PER_HZ; + + return 0; +} + +static void adf41513_chip_disable(void *data) +{ + gpiod_set_value_cansleep(data, 0); +} + +static void adf41513_close(void *data) +{ + adf41513_suspend(data); +} + +static int adf41513_setup(struct device *dev, struct adf41513_state *st) +{ + u32 tmp; + int ret; + + memset(st->regs_hw, 0xFF, sizeof(st->regs_hw)); + + /* assuming DLD pin is used for lock detection */ + st->regs[ADF41513_REG5] = FIELD_PREP(ADF41513_REG5_DLD_MODES_MSK, + ADF41513_DLD_DIG_LD); + + tmp = DIV_ROUND_CLOSEST(st->data.charge_pump_voltage_mv, ADF41513_MIN_CP_VOLTAGE_mV); + st->regs[ADF41513_REG5] |= FIELD_PREP(ADF41513_REG5_CP_CURRENT_MSK, tmp - 1); + + st->regs[ADF41513_REG6] = ADF41513_REG6_ABP_MSK | + ADF41513_REG6_LOL_ENABLE_MSK | + ADF41513_REG6_SD_RESET_MSK; + if (st->data.phase_detector_polarity) + st->regs[ADF41513_REG6] |= ADF41513_REG6_PD_POLARITY_MSK; + + st->regs[ADF41513_REG7] = FIELD_PREP(ADF41513_REG7_PS_BIAS_MSK, + ADF41513_PS_BIAS_INIT); + tmp = ilog2(st->data.lock_detect_count); + if (st->data.lock_detect_count < ADF41513_LD_COUNT_FAST_LIMIT) { + tmp -= const_ilog2(ADF41513_LD_COUNT_FAST_MIN); + st->regs[ADF41513_REG7] |= ADF41513_REG7_LD_CLK_SEL_MSK; + } else { + tmp -= const_ilog2(ADF41513_LD_COUNT_MIN); + } + st->regs[ADF41513_REG7] |= FIELD_PREP(ADF41513_REG7_LD_COUNT_MSK, tmp); + + st->regs[ADF41513_REG11] = ADF41513_REG11_POWER_DOWN_SEL_MSK; + st->regs[ADF41513_REG12] = FIELD_PREP(ADF41513_REG12_LOGIC_LEVEL_MSK, + st->data.logic_lvl_1v8_en ? 0 : 1); + + /* perform initialization sequence with power-up frequency */ + ret = adf41513_set_frequency(st, st->data.power_up_frequency_hz * MICRO, + ADF41513_SYNC_ALL); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, adf41513_close, st); + if (ret) + return ret; + + if (st->data.le_sync_en) { + st->regs[ADF41513_REG12] |= ADF41513_REG12_LE_SELECT_MSK; + ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF); + if (ret) + return ret; + } + + return 0; +} + +static int adf41513_pm_suspend(struct device *dev) +{ + struct adf41513_state *st = dev_get_drvdata(dev); + + guard(mutex)(&st->lock); + return adf41513_suspend(st); +} + +static int adf41513_pm_resume(struct device *dev) +{ + struct adf41513_state *st = dev_get_drvdata(dev); + + guard(mutex)(&st->lock); + if (st->powerdown) + return 0; /* nothing to do */ + + return adf41513_resume(st); +} + +static const struct adf41513_chip_info adf41510_chip_info = { + .name = "adf41510", + .max_rf_freq_hz = ADF41510_MAX_RF_FREQ_HZ, + .has_prescaler_8_9 = false, +}; + +static const struct adf41513_chip_info adf41513_chip_info = { + .name = "adf41513", + .max_rf_freq_hz = ADF41513_MAX_RF_FREQ_HZ, + .has_prescaler_8_9 = true, +}; + +static int adf41513_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct gpio_desc *chip_enable; + struct iio_dev *indio_dev; + struct adf41513_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->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -EINVAL; + + spi_set_drvdata(spi, st); + + st->ref_clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(st->ref_clk)) + return PTR_ERR(st->ref_clk); + + st->ref_freq_hz = clk_get_rate(st->ref_clk); + if (st->ref_freq_hz < ADF41513_MIN_REF_FREQ_HZ || + st->ref_freq_hz > ADF41513_MAX_REF_FREQ_HZ) + return dev_err_probe(dev, -ERANGE, + "reference frequency %u Hz out of range\n", + st->ref_freq_hz); + + ret = adf41513_parse_fw(st); + if (ret) + return ret; + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(adf41513_power_supplies), + adf41513_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable regulators\n"); + + st->lock_detect = devm_gpiod_get_optional(dev, "lock-detect", GPIOD_IN); + if (IS_ERR(st->lock_detect)) + return dev_err_probe(dev, PTR_ERR(st->lock_detect), + "fail to request lock detect GPIO\n"); + + chip_enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(chip_enable)) + return dev_err_probe(dev, PTR_ERR(chip_enable), + "fail to request chip enable GPIO\n"); + + ret = devm_add_action_or_reset(dev, adf41513_chip_disable, chip_enable); + if (ret) + return dev_err_probe(dev, ret, "Failed to add disable action\n"); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &adf41513_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &adf41513_chan; + indio_dev->num_channels = 1; + + ret = adf41513_setup(dev, st); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to setup device\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id adf41513_id[] = { + { .name = "adf41510", .driver_data = (kernel_ulong_t)&adf41510_chip_info }, + { .name = "adf41513", .driver_data = (kernel_ulong_t)&adf41513_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, adf41513_id); + +static const struct of_device_id adf41513_of_match[] = { + { .compatible = "adi,adf41510", .data = &adf41510_chip_info }, + { .compatible = "adi,adf41513", .data = &adf41513_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, adf41513_of_match); + +static DEFINE_SIMPLE_DEV_PM_OPS(adf41513_pm_ops, adf41513_pm_suspend, adf41513_pm_resume); + +static struct spi_driver adf41513_driver = { + .driver = { + .name = "adf41513", + .pm = pm_ptr(&adf41513_pm_ops), + .of_match_table = adf41513_of_match, + }, + .probe = adf41513_probe, + .id_table = adf41513_id, +}; +module_spi_driver(adf41513_driver); + +MODULE_AUTHOR("Rodrigo Alencar <rodrigo.alencar@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADF41513 PLL Frequency Synthesizer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 639cac522b44..315317d6eec4 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -691,8 +691,8 @@ static const struct of_device_id adf4350_of_match[] = { MODULE_DEVICE_TABLE(of, adf4350_of_match); static const struct spi_device_id adf4350_id[] = { - {"adf4350", 4350}, - {"adf4351", 4351}, + { .name = "adf4350", .driver_data = 4350 }, + { .name = "adf4351", .driver_data = 4351 }, { } }; MODULE_DEVICE_TABLE(spi, adf4350_id); diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index d6dc7827fb41..e20ed718d01d 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -624,8 +624,8 @@ static int adf4371_probe(struct spi_device *spi) } static const struct spi_device_id adf4371_id_table[] = { - { "adf4371", (kernel_ulong_t)&adf4371_chip_info }, - { "adf4372", (kernel_ulong_t)&adf4372_chip_info }, + { .name = "adf4371", .driver_data = (kernel_ulong_t)&adf4371_chip_info }, + { .name = "adf4372", .driver_data = (kernel_ulong_t)&adf4372_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adf4371_id_table); diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index f024680492b8..7385be62fa6a 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -7,6 +7,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/clkdev.h> @@ -518,14 +519,15 @@ static int adf4377_get_freq(struct adf4377_state *st, u64 *freq) u64 clkin_freq; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + ret = regmap_read(st->regmap, 0x12, &ref_div_factor); if (ret) - goto exit; + return ret; ret = regmap_bulk_read(st->regmap, 0x10, st->buf, sizeof(st->buf)); if (ret) - goto exit; + return ret; clkin_freq = clk_get_rate(st->clkin); ref_div_factor = FIELD_GET(ADF4377_0012_R_DIV_MSK, ref_div_factor); @@ -533,10 +535,8 @@ static int adf4377_get_freq(struct adf4377_state *st, u64 *freq) get_unaligned_le16(&st->buf)); *freq = div_u64(clkin_freq, ref_div_factor) * n_int; -exit: - mutex_unlock(&st->lock); - return ret; + return 0; } static int adf4377_set_freq(struct adf4377_state *st, u64 freq) @@ -545,26 +545,24 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) u64 f_vco; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); - if (freq > ADF4377_MAX_CLKPN_FREQ || freq < ADF4377_MIN_CLKPN_FREQ) { - ret = -EINVAL; - goto exit; - } + if (freq > ADF4377_MAX_CLKPN_FREQ || freq < ADF4377_MIN_CLKPN_FREQ) + return -EINVAL; ret = regmap_update_bits(st->regmap, 0x1C, ADF4377_001C_EN_DNCLK_MSK | ADF4377_001C_EN_DRCLK_MSK, FIELD_PREP(ADF4377_001C_EN_DNCLK_MSK, 1) | FIELD_PREP(ADF4377_001C_EN_DRCLK_MSK, 1)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x11, ADF4377_0011_EN_AUTOCAL_MSK | ADF4377_0011_DCLK_DIV2_MSK, FIELD_PREP(ADF4377_0011_EN_AUTOCAL_MSK, 1) | FIELD_PREP(ADF4377_0011_DCLK_DIV2_MSK, st->dclk_div2)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x2E, ADF4377_002E_EN_ADC_CNV_MSK | ADF4377_002E_EN_ADC_MSK | @@ -574,56 +572,56 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) FIELD_PREP(ADF4377_002E_ADC_A_CONV_MSK, ADF4377_002E_ADC_A_CONV_VCO_CALIB)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x20, ADF4377_0020_EN_ADC_CLK_MSK, FIELD_PREP(ADF4377_0020_EN_ADC_CLK_MSK, 1)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x2F, ADF4377_002F_DCLK_DIV1_MSK, FIELD_PREP(ADF4377_002F_DCLK_DIV1_MSK, st->dclk_div1)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x24, ADF4377_0024_DCLK_MODE_MSK, FIELD_PREP(ADF4377_0024_DCLK_MODE_MSK, st->dclk_mode)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x27, FIELD_PREP(ADF4377_0027_SYNTH_LOCK_TO_LSB_MSK, st->synth_lock_timeout)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x28, ADF4377_0028_SYNTH_LOCK_TO_MSB_MSK, FIELD_PREP(ADF4377_0028_SYNTH_LOCK_TO_MSB_MSK, st->synth_lock_timeout >> 8)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x29, FIELD_PREP(ADF4377_0029_VCO_ALC_TO_LSB_MSK, st->vco_alc_timeout)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x2A, ADF4377_002A_VCO_ALC_TO_MSB_MSK, FIELD_PREP(ADF4377_002A_VCO_ALC_TO_MSB_MSK, st->vco_alc_timeout >> 8)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x26, FIELD_PREP(ADF4377_0026_VCO_BAND_DIV_MSK, st->vco_band_div)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x2D, FIELD_PREP(ADF4377_002D_ADC_CLK_DIV_MSK, st->adc_clk_div)); if (ret) - goto exit; + return ret; st->clkout_div_sel = 0; @@ -641,24 +639,24 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) FIELD_PREP(ADF4377_0011_EN_RDBLR_MSK, 0) | FIELD_PREP(ADF4377_0011_N_INT_MSB_MSK, st->n_int >> 8)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x12, ADF4377_0012_R_DIV_MSK | ADF4377_0012_CLKOUT_DIV_MSK, FIELD_PREP(ADF4377_0012_CLKOUT_DIV_MSK, st->clkout_div_sel) | FIELD_PREP(ADF4377_0012_R_DIV_MSK, st->ref_div_factor)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x10, FIELD_PREP(ADF4377_0010_N_INT_LSB_MSK, st->n_int)); if (ret) - goto exit; + return ret; ret = regmap_read_poll_timeout(st->regmap, 0x49, read_val, !(read_val & (ADF4377_0049_FSM_BUSY_MSK)), 200, 200 * 100); if (ret) - goto exit; + return ret; /* Disable EN_DNCLK, EN_DRCLK */ ret = regmap_update_bits(st->regmap, 0x1C, ADF4377_001C_EN_DNCLK_MSK | @@ -666,26 +664,21 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) FIELD_PREP(ADF4377_001C_EN_DNCLK_MSK, 0) | FIELD_PREP(ADF4377_001C_EN_DRCLK_MSK, 0)); if (ret) - goto exit; + return ret; /* Disable EN_ADC_CLK */ ret = regmap_update_bits(st->regmap, 0x20, ADF4377_0020_EN_ADC_CLK_MSK, FIELD_PREP(ADF4377_0020_EN_ADC_CLK_MSK, 0)); if (ret) - goto exit; + return ret; /* Set output Amplitude */ - ret = regmap_update_bits(st->regmap, 0x19, ADF4377_0019_CLKOUT2_OP_MSK | - ADF4377_0019_CLKOUT1_OP_MSK, - FIELD_PREP(ADF4377_0019_CLKOUT1_OP_MSK, - ADF4377_0019_CLKOUT_420MV) | - FIELD_PREP(ADF4377_0019_CLKOUT2_OP_MSK, - ADF4377_0019_CLKOUT_420MV)); - -exit: - mutex_unlock(&st->lock); - - return ret; + return regmap_update_bits(st->regmap, 0x19, ADF4377_0019_CLKOUT2_OP_MSK | + ADF4377_0019_CLKOUT1_OP_MSK, + FIELD_PREP(ADF4377_0019_CLKOUT1_OP_MSK, + ADF4377_0019_CLKOUT_420MV) | + FIELD_PREP(ADF4377_0019_CLKOUT2_OP_MSK, + ADF4377_0019_CLKOUT_420MV)); } static void adf4377_gpio_init(struct adf4377_state *st) @@ -919,13 +912,11 @@ static int adf4377_properties_parse(struct adf4377_state *st) static int adf4377_freq_change(struct notifier_block *nb, unsigned long action, void *data) { struct adf4377_state *st = container_of(nb, struct adf4377_state, nb); - int ret; if (action == POST_RATE_CHANGE) { - mutex_lock(&st->lock); - ret = notifier_from_errno(adf4377_init(st)); - mutex_unlock(&st->lock); - return ret; + guard(mutex)(&st->lock); + + return notifier_from_errno(adf4377_init(st)); } return NOTIFY_OK; @@ -1097,8 +1088,8 @@ static int adf4377_probe(struct spi_device *spi) } static const struct spi_device_id adf4377_id[] = { - { "adf4377", (kernel_ulong_t)&adf4377_chip_info }, - { "adf4378", (kernel_ulong_t)&adf4378_chip_info }, + { .name = "adf4377", .driver_data = (kernel_ulong_t)&adf4377_chip_info }, + { .name = "adf4378", .driver_data = (kernel_ulong_t)&adf4378_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adf4377_id); diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index b823adfb0f70..c9baaab574e3 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -619,7 +619,7 @@ static int admv1013_probe(struct spi_device *spi) } static const struct spi_device_id admv1013_id[] = { - { "admv1013", 0 }, + { .name = "admv1013" }, { } }; MODULE_DEVICE_TABLE(spi, admv1013_id); diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c index 5d36ac4bb666..d1d528975229 100644 --- a/drivers/iio/frequency/admv1014.c +++ b/drivers/iio/frequency/admv1014.c @@ -781,7 +781,7 @@ static int admv1014_probe(struct spi_device *spi) } static const struct spi_device_id admv1014_id[] = { - { "admv1014", 0 }, + { .name = "admv1014" }, { } }; MODULE_DEVICE_TABLE(spi, admv1014_id); diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index c2dc06f05c21..79a615062172 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -486,7 +486,7 @@ static int adrf6780_probe(struct spi_device *spi) } static const struct spi_device_id adrf6780_id[] = { - { "adrf6780", 0 }, + { .name = "adrf6780" }, { } }; MODULE_DEVICE_TABLE(spi, adrf6780_id); diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c index 178bba95a709..0c8bc1e569fe 100644 --- a/drivers/iio/gyro/adis16080.c +++ b/drivers/iio/gyro/adis16080.c @@ -212,8 +212,8 @@ static int adis16080_probe(struct spi_device *spi) } static const struct spi_device_id adis16080_ids[] = { - { "adis16080", ID_ADIS16080 }, - { "adis16100", ID_ADIS16100 }, + { .name = "adis16080", .driver_data = ID_ADIS16080 }, + { .name = "adis16100", .driver_data = ID_ADIS16100 }, { } }; MODULE_DEVICE_TABLE(spi, adis16080_ids); diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index 369c7428e1ef..ff335f81e9ae 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -563,10 +563,10 @@ static int adis16136_probe(struct spi_device *spi) } static const struct spi_device_id adis16136_ids[] = { - { "adis16133", ID_ADIS16133 }, - { "adis16135", ID_ADIS16135 }, - { "adis16136", ID_ADIS16136 }, - { "adis16137", ID_ADIS16137 }, + { .name = "adis16133", .driver_data = ID_ADIS16133 }, + { .name = "adis16135", .driver_data = ID_ADIS16135 }, + { .name = "adis16136", .driver_data = ID_ADIS16136 }, + { .name = "adis16137", .driver_data = ID_ADIS16137 }, { } }; MODULE_DEVICE_TABLE(spi, adis16136_ids); diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 91b9c5f18ec4..a79a8edf4c98 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -411,12 +411,12 @@ static int adis16260_probe(struct spi_device *spi) * support for the on chip filtering. */ static const struct spi_device_id adis16260_id[] = { - {"adis16260", ADIS16260}, - {"adis16265", ADIS16260}, - {"adis16266", ADIS16266}, - {"adis16250", ADIS16260}, - {"adis16255", ADIS16260}, - {"adis16251", ADIS16251}, + { .name = "adis16260", .driver_data = ADIS16260 }, + { .name = "adis16265", .driver_data = ADIS16260 }, + { .name = "adis16266", .driver_data = ADIS16266 }, + { .name = "adis16250", .driver_data = ADIS16260 }, + { .name = "adis16255", .driver_data = ADIS16260 }, + { .name = "adis16251", .driver_data = ADIS16251 }, { } }; MODULE_DEVICE_TABLE(spi, adis16260_id); diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index 35928383d4f2..563de2724396 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -585,9 +585,8 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev) ret = devm_request_irq(&st->spi->dev, st->spi->irq, &iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, "adxrs290_irq", st->dready_trig); - if (ret < 0) - return dev_err_probe(&st->spi->dev, ret, - "request irq %d failed\n", st->spi->irq); + if (ret) + return ret; ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig); if (ret) { diff --git a/drivers/iio/gyro/adxrs450.c b/drivers/iio/gyro/adxrs450.c index a1d8d3cb301b..030d042cab8b 100644 --- a/drivers/iio/gyro/adxrs450.c +++ b/drivers/iio/gyro/adxrs450.c @@ -439,8 +439,8 @@ static int adxrs450_probe(struct spi_device *spi) } static const struct spi_device_id adxrs450_id[] = { - {"adxrs450", ID_ADXRS450}, - {"adxrs453", ID_ADXRS453}, + { .name = "adxrs450", .driver_data = ID_ADXRS450 }, + { .name = "adxrs453", .driver_data = ID_ADXRS453 }, { } }; MODULE_DEVICE_TABLE(spi, adxrs450_id); diff --git a/drivers/iio/gyro/bmg160_spi.c b/drivers/iio/gyro/bmg160_spi.c index 6aecc5eb8347..14ae1d03be3d 100644 --- a/drivers/iio/gyro/bmg160_spi.c +++ b/drivers/iio/gyro/bmg160_spi.c @@ -33,12 +33,11 @@ static void bmg160_spi_remove(struct spi_device *spi) } static const struct spi_device_id bmg160_spi_id[] = { - {"bmg160", 0}, - {"bmi055_gyro", 0}, - {"bmi088_gyro", 0}, + { .name = "bmg160" }, + { .name = "bmi055_gyro" }, + { .name = "bmi088_gyro" }, { } }; - MODULE_DEVICE_TABLE(spi, bmg160_spi_id); static const struct of_device_id bmg160_of_match[] = { diff --git a/drivers/iio/gyro/fxas21002c_spi.c b/drivers/iio/gyro/fxas21002c_spi.c index bd5b8678da13..40b1e02b2a5c 100644 --- a/drivers/iio/gyro/fxas21002c_spi.c +++ b/drivers/iio/gyro/fxas21002c_spi.c @@ -39,7 +39,7 @@ static void fxas21002c_spi_remove(struct spi_device *spi) } static const struct spi_device_id fxas21002c_spi_id[] = { - { "fxas21002c", 0 }, + { .name = "fxas21002c" }, { } }; MODULE_DEVICE_TABLE(spi, fxas21002c_spi_id); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index adc52a5267f7..58250a972567 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -84,9 +84,8 @@ static const struct iio_chan_spec gyro_3d_channels[] = { /* Channel read_raw handler */ static int gyro_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct gyro_3d_state *gyro_state = iio_priv(indio_dev); int report_id = -1; @@ -111,8 +110,7 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, min < 0); else { *val = 0; - hid_sensor_power_state(&gyro_state->common_attributes, - false); + hid_sensor_power_state(&gyro_state->common_attributes, false); return -EINVAL; } hid_sensor_power_state(&gyro_state->common_attributes, false); @@ -145,10 +143,8 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int gyro_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct gyro_3d_state *gyro_state = iio_priv(indio_dev); int ret = 0; @@ -176,8 +172,7 @@ static const struct iio_info gyro_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); @@ -198,9 +193,9 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); @@ -233,7 +228,7 @@ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, static int gyro_3d_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned usage_id, + u32 usage_id, struct gyro_3d_state *st) { int ret; @@ -253,10 +248,10 @@ static int gyro_3d_parse_report(struct platform_device *pdev, }; } dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n", - st->gyro[0].index, - st->gyro[0].report_id, - st->gyro[1].index, st->gyro[1].report_id, - st->gyro[2].index, st->gyro[2].report_id); + st->gyro[0].index, + st->gyro[0].report_id, + st->gyro[1].index, st->gyro[1].report_id, + st->gyro[2].index, st->gyro[2].report_id); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_GYRO_3D, @@ -317,32 +312,32 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) atomic_set(&gyro_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &gyro_state->common_attributes); + &gyro_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - gyro_state->callbacks.send_event = gyro_3d_proc_event; gyro_state->callbacks.capture_sample = gyro_3d_capture_sample; gyro_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D, - &gyro_state->callbacks); + &gyro_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes); return ret; @@ -355,8 +350,8 @@ static void hid_gyro_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes); } diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index adfbbc0d37dc..c56331755b5c 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -97,15 +97,15 @@ static int st_gyro_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_gyro_id_table[] = { - { L3G4200D_GYRO_DEV_NAME }, - { LSM330D_GYRO_DEV_NAME }, - { LSM330DL_GYRO_DEV_NAME }, - { LSM330DLC_GYRO_DEV_NAME }, - { L3GD20_GYRO_DEV_NAME }, - { L3GD20H_GYRO_DEV_NAME }, - { L3G4IS_GYRO_DEV_NAME }, - { LSM330_GYRO_DEV_NAME }, - { LSM9DS0_GYRO_DEV_NAME }, + { .name = L3G4200D_GYRO_DEV_NAME }, + { .name = LSM330D_GYRO_DEV_NAME }, + { .name = LSM330DL_GYRO_DEV_NAME }, + { .name = LSM330DLC_GYRO_DEV_NAME }, + { .name = L3GD20_GYRO_DEV_NAME }, + { .name = L3GD20H_GYRO_DEV_NAME }, + { .name = L3G4IS_GYRO_DEV_NAME }, + { .name = LSM330_GYRO_DEV_NAME }, + { .name = LSM9DS0_GYRO_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_gyro_id_table); diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index d358f4d5e5da..2a648532242a 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -544,10 +544,8 @@ static int afe4403_probe(struct spi_device *spi) iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, AFE4403_DRIVER_NAME, afe->trig); - if (ret) { - dev_err(dev, "Unable to request IRQ\n"); + if (ret) return ret; - } } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, @@ -568,7 +566,7 @@ static int afe4403_probe(struct spi_device *spi) } static const struct spi_device_id afe4403_ids[] = { - { "afe4403", 0 }, + { .name = "afe4403" }, { } }; MODULE_DEVICE_TABLE(spi, afe4403_ids); diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 2357e2dd7017..22cd46eca217 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -551,10 +551,8 @@ static int afe4404_probe(struct i2c_client *client) iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, AFE4404_DRIVER_NAME, afe->trig); - if (ret) { - dev_err(dev, "Unable to request IRQ\n"); + if (ret) return ret; - } } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 97352485f5e2..191540e10c20 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -489,10 +489,8 @@ static int max30100_probe(struct i2c_client *client) NULL, max30100_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "max30100_irq", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + if (ret) return ret; - } return iio_device_register(indio_dev); } diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index b7402e2bb081..aee96167f01e 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -583,10 +583,8 @@ static int max30102_probe(struct i2c_client *client) NULL, max30102_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "max30102_irq", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + if (ret) return ret; - } return iio_device_register(indio_dev); } diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c index 1ae8702ada54..75971c19624a 100644 --- a/drivers/iio/humidity/hdc3020.c +++ b/drivers/iio/humidity/hdc3020.c @@ -843,8 +843,7 @@ static int hdc3020_probe(struct i2c_client *client) IRQF_ONESHOT, "hdc3020", indio_dev); if (ret) - return dev_err_probe(&client->dev, ret, - "Failed to request IRQ\n"); + return ret; } ret = devm_iio_device_register(&data->client->dev, indio_dev); diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index 5267a14d73ec..7cec81ff5685 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -44,7 +44,7 @@ static const struct iio_chan_spec humidity_channels[] = { /* Adjust channel real bits based on report descriptor */ static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) + int channel, int size) { channels[channel].scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -54,8 +54,8 @@ static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels, } static int humidity_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -100,8 +100,8 @@ static int humidity_read_raw(struct iio_dev *indio_dev, } static int humidity_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -126,7 +126,7 @@ static const struct iio_info humidity_info = { /* Callback handler to send event after all samples are received and captured */ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -140,8 +140,9 @@ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, size_t raw_len, - char *raw_data, void *pdev) + u32 usage_id, + size_t raw_len, char *raw_data, + void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -158,10 +159,10 @@ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id */ static int humidity_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - unsigned int usage_id, - struct hid_humidity_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct hid_humidity_state *st) { int ret; @@ -214,13 +215,13 @@ static int hid_humidity_probe(struct platform_device *pdev) if (ret) return ret; - humid_chans = devm_kmemdup(&indio_dev->dev, humidity_channels, - sizeof(humidity_channels), GFP_KERNEL); + humid_chans = devm_kmemdup(&pdev->dev, humidity_channels, + sizeof(humidity_channels), GFP_KERNEL); if (!humid_chans) return -ENOMEM; ret = humidity_parse_report(pdev, hsdev, humid_chans, - HID_USAGE_SENSOR_HUMIDITY, humid_st); + HID_USAGE_SENSOR_HUMIDITY, humid_st); if (ret) return ret; @@ -233,7 +234,7 @@ static int hid_humidity_probe(struct platform_device *pdev) atomic_set(&humid_st->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &humid_st->common_attributes); + &humid_st->common_attributes); if (ret) return ret; @@ -241,7 +242,7 @@ static int hid_humidity_probe(struct platform_device *pdev) humidity_callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY, - &humidity_callbacks); + &humidity_callbacks); if (ret) goto error_remove_trigger; diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 4d03db19063e..df12c7a25b2e 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -122,11 +122,8 @@ int hts221_allocate_trigger(struct iio_dev *iio_dev) hts221_trigger_handler_thread, irq_type | IRQF_ONESHOT, hw->name, hw); - if (err) { - dev_err(hw->dev, "failed to request trigger irq %d\n", - hw->irq); + if (err) return err; - } hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger", iio_dev->name); diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c index e6fef2acd523..f962842cc71d 100644 --- a/drivers/iio/humidity/hts221_spi.c +++ b/drivers/iio/humidity/hts221_spi.c @@ -47,7 +47,7 @@ static const struct of_device_id hts221_spi_of_match[] = { MODULE_DEVICE_TABLE(of, hts221_spi_of_match); static const struct spi_device_id hts221_spi_id_table[] = { - { HTS221_DEV_NAME }, + { .name = HTS221_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, hts221_spi_id_table); diff --git a/drivers/iio/iio_core_trigger.h b/drivers/iio/iio_core_trigger.h index e1a56824e07f..4698a857739f 100644 --- a/drivers/iio/iio_core_trigger.h +++ b/drivers/iio/iio_core_trigger.h @@ -56,4 +56,4 @@ static inline int iio_trigger_detach_poll_func(struct iio_trigger *trig, return 0; } -#endif /* CONFIG_TRIGGER_CONSUMER */ +#endif /* CONFIG_IIO_TRIGGER */ diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 7e0181c27bb6..8bab4616be20 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -109,6 +109,7 @@ config KMX61 be called kmx61. source "drivers/iio/imu/inv_icm42600/Kconfig" +source "drivers/iio/imu/inv_icm42607/Kconfig" source "drivers/iio/imu/inv_icm45600/Kconfig" source "drivers/iio/imu/inv_mpu6050/Kconfig" diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 13fb7846e9c9..3268dc2371ae 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o obj-y += inv_icm42600/ +obj-y += inv_icm42607/ obj-y += inv_icm45600/ obj-y += inv_mpu6050/ diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 36323ad149e0..4842346f9f0c 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -1189,21 +1189,21 @@ static int adis16400_probe(struct spi_device *spi) } static const struct spi_device_id adis16400_id[] = { - { "adis16300", (kernel_ulong_t)&adis16300_chip_info }, - { "adis16305", (kernel_ulong_t)&adis16300_chip_info }, - { "adis16334", (kernel_ulong_t)&adis16334_chip_info }, - { "adis16350", (kernel_ulong_t)&adis16350_chip_info }, - { "adis16354", (kernel_ulong_t)&adis16350_chip_info }, - { "adis16355", (kernel_ulong_t)&adis16350_chip_info }, - { "adis16360", (kernel_ulong_t)&adis16360_chip_info }, - { "adis16362", (kernel_ulong_t)&adis16362_chip_info }, - { "adis16364", (kernel_ulong_t)&adis16364_chip_info }, - { "adis16365", (kernel_ulong_t)&adis16360_chip_info }, - { "adis16367", (kernel_ulong_t)&adis16367_chip_info }, - { "adis16400", (kernel_ulong_t)&adis16400_chip_info }, - { "adis16405", (kernel_ulong_t)&adis16400_chip_info }, - { "adis16445", (kernel_ulong_t)&adis16445_chip_info }, - { "adis16448", (kernel_ulong_t)&adis16448_chip_info }, + { .name = "adis16300", .driver_data = (kernel_ulong_t)&adis16300_chip_info }, + { .name = "adis16305", .driver_data = (kernel_ulong_t)&adis16300_chip_info }, + { .name = "adis16334", .driver_data = (kernel_ulong_t)&adis16334_chip_info }, + { .name = "adis16350", .driver_data = (kernel_ulong_t)&adis16350_chip_info }, + { .name = "adis16354", .driver_data = (kernel_ulong_t)&adis16350_chip_info }, + { .name = "adis16355", .driver_data = (kernel_ulong_t)&adis16350_chip_info }, + { .name = "adis16360", .driver_data = (kernel_ulong_t)&adis16360_chip_info }, + { .name = "adis16362", .driver_data = (kernel_ulong_t)&adis16362_chip_info }, + { .name = "adis16364", .driver_data = (kernel_ulong_t)&adis16364_chip_info }, + { .name = "adis16365", .driver_data = (kernel_ulong_t)&adis16360_chip_info }, + { .name = "adis16367", .driver_data = (kernel_ulong_t)&adis16367_chip_info }, + { .name = "adis16400", .driver_data = (kernel_ulong_t)&adis16400_chip_info }, + { .name = "adis16405", .driver_data = (kernel_ulong_t)&adis16400_chip_info }, + { .name = "adis16445", .driver_data = (kernel_ulong_t)&adis16445_chip_info }, + { .name = "adis16448", .driver_data = (kernel_ulong_t)&adis16448_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adis16400_id); diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index ba1887d36577..cadf21c05bbf 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -394,7 +394,7 @@ static int adis16460_probe(struct spi_device *spi) } static const struct spi_device_id adis16460_ids[] = { - { "adis16460", 0 }, + { .name = "adis16460" }, { } }; MODULE_DEVICE_TABLE(spi, adis16460_ids); diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 17335386d8e3..5f21dceda8f7 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -2061,33 +2061,33 @@ static const struct of_device_id adis16475_of_match[] = { MODULE_DEVICE_TABLE(of, adis16475_of_match); static const struct spi_device_id adis16475_ids[] = { - { "adis16470", (kernel_ulong_t)&adis16475_chip_info[ADIS16470] }, - { "adis16475-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] }, - { "adis16475-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] }, - { "adis16475-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] }, - { "adis16477-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] }, - { "adis16477-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] }, - { "adis16477-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] }, - { "adis16465-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] }, - { "adis16465-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] }, - { "adis16465-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] }, - { "adis16467-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] }, - { "adis16467-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] }, - { "adis16467-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] }, - { "adis16500", (kernel_ulong_t)&adis16475_chip_info[ADIS16500] }, - { "adis16501", (kernel_ulong_t)&adis16475_chip_info[ADIS16501] }, - { "adis16505-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] }, - { "adis16505-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] }, - { "adis16505-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] }, - { "adis16507-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] }, - { "adis16507-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] }, - { "adis16507-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] }, - { "adis16575-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16575_2] }, - { "adis16575-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16575_3] }, - { "adis16576-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16576_2] }, - { "adis16576-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16576_3] }, - { "adis16577-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16577_2] }, - { "adis16577-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16577_3] }, + { .name = "adis16470", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16470] }, + { .name = "adis16475-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] }, + { .name = "adis16475-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] }, + { .name = "adis16475-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] }, + { .name = "adis16477-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] }, + { .name = "adis16477-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] }, + { .name = "adis16477-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] }, + { .name = "adis16465-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] }, + { .name = "adis16465-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] }, + { .name = "adis16465-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] }, + { .name = "adis16467-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] }, + { .name = "adis16467-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] }, + { .name = "adis16467-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] }, + { .name = "adis16500", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16500] }, + { .name = "adis16501", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16501] }, + { .name = "adis16505-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] }, + { .name = "adis16505-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] }, + { .name = "adis16505-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] }, + { .name = "adis16507-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] }, + { .name = "adis16507-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] }, + { .name = "adis16507-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] }, + { .name = "adis16575-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16575_2] }, + { .name = "adis16575-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16575_3] }, + { .name = "adis16576-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16576_2] }, + { .name = "adis16576-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16576_3] }, + { .name = "adis16577-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16577_2] }, + { .name = "adis16577-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16577_3] }, { } }; MODULE_DEVICE_TABLE(spi, adis16475_ids); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index e009f3824768..5187566c1876 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -110,7 +110,7 @@ #define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20) -/* Each filter coefficent bank spans two pages */ +/* Each filter coefficient bank spans two pages */ #define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \ ADIS16480_REG((page) + 1, (x) - 60 + 8)) #define ADIS16480_FIR_COEF_A(x) ADIS16480_FIR_COEF(0x05, (x)) @@ -1467,7 +1467,7 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p) * We need to perform the padding to have the buffer * elements naturally aligned in case there are any * 32-bit storage size channels enabled which are added - * in the buffer after the temprature data. In case + * in the buffer after the temperature data. In case * there is no data being added after the temperature * data, the padding is harmless. */ @@ -1806,26 +1806,26 @@ static int adis16480_probe(struct spi_device *spi) } static const struct spi_device_id adis16480_ids[] = { - { "adis16375", ADIS16375 }, - { "adis16480", ADIS16480 }, - { "adis16485", ADIS16485 }, - { "adis16486", ADIS16486 }, - { "adis16487", ADIS16487 }, - { "adis16488", ADIS16488 }, - { "adis16489", ADIS16489 }, - { "adis16490", ADIS16490 }, - { "adis16495-1", ADIS16495_1 }, - { "adis16495-2", ADIS16495_2 }, - { "adis16495-3", ADIS16495_3 }, - { "adis16497-1", ADIS16497_1 }, - { "adis16497-2", ADIS16497_2 }, - { "adis16497-3", ADIS16497_3 }, - { "adis16545-1", ADIS16545_1 }, - { "adis16545-2", ADIS16545_2 }, - { "adis16545-3", ADIS16545_3 }, - { "adis16547-1", ADIS16547_1 }, - { "adis16547-2", ADIS16547_2 }, - { "adis16547-3", ADIS16547_3 }, + { .name = "adis16375", .driver_data = ADIS16375 }, + { .name = "adis16480", .driver_data = ADIS16480 }, + { .name = "adis16485", .driver_data = ADIS16485 }, + { .name = "adis16486", .driver_data = ADIS16486 }, + { .name = "adis16487", .driver_data = ADIS16487 }, + { .name = "adis16488", .driver_data = ADIS16488 }, + { .name = "adis16489", .driver_data = ADIS16489 }, + { .name = "adis16490", .driver_data = ADIS16490 }, + { .name = "adis16495-1", .driver_data = ADIS16495_1 }, + { .name = "adis16495-2", .driver_data = ADIS16495_2 }, + { .name = "adis16495-3", .driver_data = ADIS16495_3 }, + { .name = "adis16497-1", .driver_data = ADIS16497_1 }, + { .name = "adis16497-2", .driver_data = ADIS16497_2 }, + { .name = "adis16497-3", .driver_data = ADIS16497_3 }, + { .name = "adis16545-1", .driver_data = ADIS16545_1 }, + { .name = "adis16545-2", .driver_data = ADIS16545_2 }, + { .name = "adis16545-3", .driver_data = ADIS16545_3 }, + { .name = "adis16547-1", .driver_data = ADIS16547_1 }, + { .name = "adis16547-2", .driver_data = ADIS16547_2 }, + { .name = "adis16547-3", .driver_data = ADIS16547_3 }, { } }; MODULE_DEVICE_TABLE(spi, adis16480_ids); diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c index 1e435d60cd6d..096a612865c4 100644 --- a/drivers/iio/imu/adis16550.c +++ b/drivers/iio/imu/adis16550.c @@ -88,25 +88,7 @@ struct adis16550_sync { u16 max_rate; }; -struct adis16550_chip_info { - const struct iio_chan_spec *channels; - const struct adis16550_sync *sync_mode; - char *name; - u32 num_channels; - u32 gyro_max_val; - u32 gyro_max_scale; - u32 accel_max_val; - u32 accel_max_scale; - u32 temp_scale; - u32 deltang_max_val; - u32 deltvel_max_val; - u32 int_clk; - u16 max_dec; - u16 num_sync; -}; - struct adis16550 { - const struct adis16550_chip_info *info; struct adis adis; unsigned long clk_freq_hz; u32 sync_mode; @@ -449,8 +431,8 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz) * The optimal sample rate for the supported IMUs is between * int_clk - 1000 and int_clk + 500. */ - u32 max_sample_rate = st->info->int_clk * 1000 + 500000; - u32 min_sample_rate = st->info->int_clk * 1000 - 1000000; + u32 max_sample_rate = 4000 * 1000 + 500000; + u32 min_sample_rate = 4000 * 1000 - 1000000; if (!freq_hz) return -EINVAL; @@ -483,7 +465,7 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz) if (dec) dec--; - dec = min(dec, st->info->max_dec); + dec = min(dec, 4095); return __adis_write_reg_16(&st->adis, ADIS16550_REG_DEC_RATE, dec); } @@ -591,30 +573,30 @@ static int adis16550_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL_VEL: - *val = st->info->gyro_max_val; - *val2 = st->info->gyro_max_scale; + *val = 1; + *val2 = IIO_RAD_TO_DEGREE(80 << 16); return IIO_VAL_FRACTIONAL; case IIO_ACCEL: - *val = st->info->accel_max_val; - *val2 = st->info->accel_max_scale; + *val = 1; + *val2 = IIO_M_S_2_TO_G(102400000); return IIO_VAL_FRACTIONAL; case IIO_TEMP: - *val = st->info->temp_scale; + *val = 4; return IIO_VAL_INT; case IIO_DELTA_ANGL: - *val = st->info->deltang_max_val; + *val = IIO_DEGREE_TO_RAD(720); *val2 = 31; return IIO_VAL_FRACTIONAL_LOG2; case IIO_DELTA_VELOCITY: - *val = st->info->deltvel_max_val; + *val = 125; *val2 = 31; return IIO_VAL_FRACTIONAL_LOG2; default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: - /* temperature centered at 25°C */ - *val = DIV_ROUND_CLOSEST(25000, st->info->temp_scale); + /* temperature centered at 25°C divided by temp scale */ + *val = 25000 / 4; return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBBIAS: ret = adis_read_reg_32(&st->adis, @@ -792,23 +774,6 @@ static const struct adis16550_sync adis16550_sync_modes[] = { { ADIS16550_SYNC_MODE_SCALED, 1, 128 }, }; -static const struct adis16550_chip_info adis16550_chip_info = { - .num_channels = ARRAY_SIZE(adis16550_channels), - .channels = adis16550_channels, - .name = "adis16550", - .gyro_max_val = 1, - .gyro_max_scale = IIO_RAD_TO_DEGREE(80 << 16), - .accel_max_val = 1, - .accel_max_scale = IIO_M_S_2_TO_G(102400000), - .temp_scale = 4, - .deltang_max_val = IIO_DEGREE_TO_RAD(720), - .deltvel_max_val = 125, - .int_clk = 4000, - .max_dec = 4095, - .sync_mode = adis16550_sync_modes, - .num_sync = ARRAY_SIZE(adis16550_sync_modes), -}; - static u32 adis16550_validate_crc(__be32 *buffer, const u8 n_elem) { int i; @@ -917,21 +882,21 @@ static int adis16550_config_sync(struct adis16550 *st) if (IS_ERR(clk)) return PTR_ERR(clk); if (!clk) { - st->clk_freq_hz = st->info->int_clk * 1000; + st->clk_freq_hz = 4000000; return 0; } st->clk_freq_hz = clk_get_rate(clk); - for (i = 0; i < st->info->num_sync; i++) { - if (st->clk_freq_hz >= st->info->sync_mode[i].min_rate && - st->clk_freq_hz <= st->info->sync_mode[i].max_rate) { - sync_mode_data = &st->info->sync_mode[i]; + for (i = 0; i < ARRAY_SIZE(adis16550_sync_modes); i++) { + if (st->clk_freq_hz >= adis16550_sync_modes[i].min_rate && + st->clk_freq_hz <= adis16550_sync_modes[i].max_rate) { + sync_mode_data = &adis16550_sync_modes[i]; break; } } - if (i == st->info->num_sync) + if (i == ARRAY_SIZE(adis16550_sync_modes)) return dev_err_probe(dev, -EINVAL, "Clk rate: %lu not in a valid range", st->clk_freq_hz); @@ -942,7 +907,7 @@ static int adis16550_config_sync(struct adis16550 *st) * of [3000 4500]. */ - sync_scale = DIV_ROUND_CLOSEST(st->info->int_clk, st->clk_freq_hz); + sync_scale = DIV_ROUND_CLOSEST(4000, st->clk_freq_hz); if (3000 > sync_scale || 4500 < sync_scale) return dev_err_probe(dev, -EINVAL, @@ -954,7 +919,7 @@ static int adis16550_config_sync(struct adis16550 *st) if (ret) return ret; - st->clk_freq_hz = st->info->int_clk; + st->clk_freq_hz = 4000; } st->clk_freq_hz *= 1000; @@ -1063,13 +1028,11 @@ static int adis16550_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - st->info = spi_get_device_match_data(spi); - if (!st->info) - return -EINVAL; + adis = &st->adis; - indio_dev->name = st->info->name; - indio_dev->channels = st->info->channels; - indio_dev->num_channels = st->info->num_channels; + indio_dev->name = "adis16550"; + indio_dev->channels = adis16550_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16550_channels); indio_dev->available_scan_masks = adis16550_channel_masks; indio_dev->info = &adis16550_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1116,13 +1079,13 @@ static int adis16550_probe(struct spi_device *spi) } static const struct spi_device_id adis16550_id[] = { - { "adis16550", (kernel_ulong_t)&adis16550_chip_info}, + { .name = "adis16550" }, { } }; MODULE_DEVICE_TABLE(spi, adis16550_id); static const struct of_device_id adis16550_of_match[] = { - { .compatible = "adi,adis16550", .data = &adis16550_chip_info }, + { .compatible = "adi,adis16550" }, { } }; MODULE_DEVICE_TABLE(of, adis16550_of_match); diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 86f6ecfd64aa..1e73a7f1f017 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -5,7 +5,7 @@ * Copyright (c) 2016, Intel Corporation. * Copyright (c) 2019, Martin Kelly. * - * IIO core driver for BMI160, with support for I2C/SPI busses + * IIO core driver for BMI160, with support for I2C/SPI buses * * TODO: magnetometer, hardware FIFO */ diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c index 2f0a578ee40f..107eb3374922 100644 --- a/drivers/iio/imu/bmi160/bmi160_spi.c +++ b/drivers/iio/imu/bmi160/bmi160_spi.c @@ -34,8 +34,8 @@ static int bmi160_spi_probe(struct spi_device *spi) } static const struct spi_device_id bmi160_spi_id[] = { - {"bmi120", 0}, - {"bmi160", 0}, + { .name = "bmi120" }, + { .name = "bmi160" }, { } }; MODULE_DEVICE_TABLE(spi, bmi160_spi_id); diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c index 2ad230788532..fbcf6bb87acc 100644 --- a/drivers/iio/imu/bmi270/bmi270_core.c +++ b/drivers/iio/imu/bmi270/bmi270_core.c @@ -1434,7 +1434,7 @@ static int bmi270_trigger_probe(struct bmi270_data *data, bmi270_irq_thread_handler, IRQF_ONESHOT, "bmi270-int", indio_dev); if (ret) - return dev_err_probe(data->dev, ret, "Failed to request IRQ\n"); + return ret; ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) diff --git a/drivers/iio/imu/bmi270/bmi270_spi.c b/drivers/iio/imu/bmi270/bmi270_spi.c index dc7fa01421bc..759b56b59e54 100644 --- a/drivers/iio/imu/bmi270/bmi270_spi.c +++ b/drivers/iio/imu/bmi270/bmi270_spi.c @@ -65,8 +65,8 @@ static int bmi270_spi_probe(struct spi_device *spi) } static const struct spi_device_id bmi270_spi_id[] = { - { "bmi260", (kernel_ulong_t)&bmi260_chip_info }, - { "bmi270", (kernel_ulong_t)&bmi270_chip_info }, + { .name = "bmi260", .driver_data = (kernel_ulong_t)&bmi260_chip_info }, + { .name = "bmi270", .driver_data = (kernel_ulong_t)&bmi270_chip_info }, { } }; @@ -75,6 +75,7 @@ static const struct of_device_id bmi270_of_match[] = { { .compatible = "bosch,bmi270", .data = &bmi270_chip_info }, { } }; +MODULE_DEVICE_TABLE(of, bmi270_of_match); static struct spi_driver bmi270_spi_driver = { .driver = { diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index f3d499423399..ebeb1b10c38c 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -1128,7 +1128,7 @@ static int bmi323_set_watermark(struct iio_dev *indio_dev, unsigned int val) { struct bmi323_data *data = iio_priv(indio_dev); - val = min(val, (u32)BMI323_FIFO_FULL_IN_FRAMES); + val = min(val, BMI323_FIFO_FULL_IN_FRAMES); guard(mutex)(&data->mutex); data->watermark = val; @@ -1946,7 +1946,7 @@ static int bmi323_trigger_probe(struct bmi323_data *data, bmi323_irq_thread_handler, IRQF_ONESHOT, "bmi323-int", indio_dev); if (ret) - return dev_err_probe(data->dev, ret, "Failed to request IRQ\n"); + return ret; ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) diff --git a/drivers/iio/imu/bmi323/bmi323_spi.c b/drivers/iio/imu/bmi323/bmi323_spi.c index 92f1c4bcc192..ed0235cc40da 100644 --- a/drivers/iio/imu/bmi323/bmi323_spi.c +++ b/drivers/iio/imu/bmi323/bmi323_spi.c @@ -64,7 +64,7 @@ static int bmi323_spi_probe(struct spi_device *spi) } static const struct spi_device_id bmi323_spi_ids[] = { - { "bmi323" }, + { .name = "bmi323" }, { } }; MODULE_DEVICE_TABLE(spi, bmi323_spi_ids); diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c index 281ebfd9c15a..9d2eb6fbab4a 100644 --- a/drivers/iio/imu/fxos8700_core.c +++ b/drivers/iio/imu/fxos8700_core.c @@ -2,7 +2,7 @@ /* * FXOS8700 - NXP IMU (accelerometer plus magnetometer) * - * IIO core driver for FXOS8700, with support for I2C/SPI busses + * IIO core driver for FXOS8700, with support for I2C/SPI buses * * TODO: Buffer, trigger, and IRQ support */ diff --git a/drivers/iio/imu/fxos8700_spi.c b/drivers/iio/imu/fxos8700_spi.c index 3edf90220bfa..56ad0c5914d9 100644 --- a/drivers/iio/imu/fxos8700_spi.c +++ b/drivers/iio/imu/fxos8700_spi.c @@ -23,7 +23,7 @@ static int fxos8700_spi_probe(struct spi_device *spi) } static const struct spi_device_id fxos8700_spi_id[] = { - {"fxos8700", 0}, + { .name = "fxos8700" }, { } }; MODULE_DEVICE_TABLE(spi, fxos8700_spi_id); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index c8b48a5c5ed0..b55d993f0264 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -6,13 +6,15 @@ #ifndef INV_ICM42600_H_ #define INV_ICM42600_H_ -#include <linux/bits.h> #include <linux/bitfield.h> -#include <linux/regmap.h> +#include <linux/bits.h> #include <linux/mutex.h> -#include <linux/regulator/consumer.h> #include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + #include <linux/iio/iio.h> + #include <linux/iio/common/inv_sensors_timestamp.h> #include "inv_icm42600_buffer.h" @@ -354,6 +356,8 @@ struct inv_icm42600_sensor_state { cpu_to_le16((_wm) & GENMASK(11, 0)) /* FIFO is 2048 bytes, let 12 samples for reading latency */ #define INV_ICM42600_FIFO_WATERMARK_MAX (2048 - 12 * 16) +/* INV_ICM42600_FIFO_WATERMARK_MAX / 8 = 232 */ +#define INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES 232 #define INV_ICM42600_REG_INT_CONFIG1 0x0064 #define INV_ICM42600_INT_CONFIG1_TPULSE_DURATION BIT(6) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 7df920ef3cf0..4b0e3cd8a506 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -3,25 +3,26 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/minmax.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> -#include <linux/delay.h> -#include <linux/math64.h> -#include <linux/minmax.h> #include <linux/units.h> #include <linux/iio/buffer.h> -#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> +#include <linux/iio/common/inv_sensors_timestamp.h> + #include "inv_icm42600.h" -#include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" +#include "inv_icm42600_temp.h" #define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \ { \ @@ -1186,8 +1187,9 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels); indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks; - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - &inv_icm42600_buffer_ops); + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &inv_icm42600_buffer_ops, + inv_icm42600_buffer_attrs); if (ret) return ERR_PTR(ret); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 5c3840acf085..998d312f7bde 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -3,17 +3,20 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/kernel.h> #include <linux/minmax.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> -#include <linux/delay.h> +#include <linux/stringify.h> #include <linux/iio/buffer.h> -#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include <linux/iio/common/inv_sensors_timestamp.h> #include "inv_icm42600.h" #include "inv_icm42600_buffer.h" @@ -186,7 +189,7 @@ static unsigned int inv_icm42600_wm_truncate(unsigned int watermark, * smallest latency but this is not as simple as choosing the smallest watermark * value. Latency depends on watermark and ODR. It requires several steps: * 1) compute gyro and accel latencies and choose the smallest value. - * 2) adapt the choosen latency so that it is a multiple of both gyro and accel + * 2) adapt the chosen latency so that it is a multiple of both gyro and accel * ones. Otherwise it is possible that you don't meet a requirement. (for * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the * value of 4 will not meet accel latency requirement because 6 is not a @@ -438,6 +441,40 @@ const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = { .postdisable = inv_icm42600_buffer_postdisable, }; +static ssize_t hwfifo_watermark_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + unsigned int wm; + + guard(mutex)(&st->lock); + + if (indio_dev == st->indio_accel) + wm = st->fifo.watermark.eff_accel; + else if (indio_dev == st->indio_gyro) + wm = st->fifo.watermark.eff_gyro; + else + return -EINVAL; + + return sysfs_emit(buf, "%u\n", wm); +} + +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_min, "1"); +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_max, + __stringify(INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES)); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0); +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_enabled, "1"); + +const struct iio_dev_attr *inv_icm42600_buffer_attrs[] = { + &iio_dev_attr_hwfifo_watermark_min, + &iio_dev_attr_hwfifo_watermark_max, + &iio_dev_attr_hwfifo_watermark, + &iio_dev_attr_hwfifo_enabled, + NULL +}; + int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, unsigned int max) { diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index 88b8b9f780af..e37ee3416337 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -6,8 +6,8 @@ #ifndef INV_ICM42600_BUFFER_H_ #define INV_ICM42600_BUFFER_H_ -#include <linux/kernel.h> #include <linux/bits.h> +#include <linux/kernel.h> struct inv_icm42600_state; @@ -80,6 +80,7 @@ ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel, const void **timestamp, unsigned int *odr); extern const struct iio_buffer_setup_ops inv_icm42600_buffer_ops; +extern const struct iio_dev_attr *inv_icm42600_buffer_attrs[]; int inv_icm42600_buffer_init(struct inv_icm42600_state *st); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 76eb22488e5f..dc97d8a274e3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -3,18 +3,18 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> -#include <linux/device.h> -#include <linux/module.h> -#include <linux/slab.h> #include <linux/delay.h> -#include <linux/mutex.h> +#include <linux/device.h> #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/regulator/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/property.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index a18dcac93929..253bf571439d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -3,22 +3,23 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/kernel.h> +#include <linux/math64.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> -#include <linux/delay.h> -#include <linux/math64.h> #include <linux/iio/buffer.h> -#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> +#include <linux/iio/common/inv_sensors_timestamp.h> + #include "inv_icm42600.h" -#include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" +#include "inv_icm42600_temp.h" #define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \ { \ @@ -772,8 +773,9 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks; indio_dev->setup_ops = &inv_icm42600_buffer_ops; - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - &inv_icm42600_buffer_ops); + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &inv_icm42600_buffer_ops, + inv_icm42600_buffer_attrs); if (ret) return ERR_PTR(ret); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 1013aff4f0ab..28552d2db91d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -3,12 +3,12 @@ * Copyright (C) 2020 InvenSense, Inc. */ -#include <linux/kernel.h> #include <linux/device.h> -#include <linux/module.h> #include <linux/i2c.h> -#include <linux/regmap.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/property.h> +#include <linux/regmap.h> #include "inv_icm42600.h" diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 57e3c448dca1..faf743bc6444 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -3,12 +3,12 @@ * Copyright (C) 2020 InvenSense, Inc. */ -#include <linux/kernel.h> #include <linux/device.h> +#include <linux/kernel.h> #include <linux/module.h> -#include <linux/spi/spi.h> -#include <linux/regmap.h> #include <linux/property.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> #include "inv_icm42600.h" @@ -71,13 +71,13 @@ static int inv_icm42600_probe(struct spi_device *spi) * supported by this driver */ static const struct spi_device_id inv_icm42600_id[] = { - { "icm42600", INV_CHIP_ICM42600 }, - { "icm42602", INV_CHIP_ICM42602 }, - { "icm42605", INV_CHIP_ICM42605 }, - { "icm42686", INV_CHIP_ICM42686 }, - { "icm42622", INV_CHIP_ICM42622 }, - { "icm42688", INV_CHIP_ICM42688 }, - { "icm42631", INV_CHIP_ICM42631 }, + { .name = "icm42600", .driver_data = INV_CHIP_ICM42600 }, + { .name = "icm42602", .driver_data = INV_CHIP_ICM42602 }, + { .name = "icm42605", .driver_data = INV_CHIP_ICM42605 }, + { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 }, + { .name = "icm42622", .driver_data = INV_CHIP_ICM42622 }, + { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 }, + { .name = "icm42631", .driver_data = INV_CHIP_ICM42631 }, { } }; MODULE_DEVICE_TABLE(spi, inv_icm42600_id); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c index 727b03d541a5..7f80bda471c3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c @@ -3,11 +3,12 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> #include <linux/device.h> +#include <linux/kernel.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> + #include <linux/iio/iio.h> #include "inv_icm42600.h" diff --git a/drivers/iio/imu/inv_icm42607/Kconfig b/drivers/iio/imu/inv_icm42607/Kconfig new file mode 100644 index 000000000000..23f461f57afc --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config INV_ICM42607 + tristate + select IIO_BUFFER + select IIO_INV_SENSORS_TIMESTAMP + +config INV_ICM42607_I2C + tristate "InvenSense ICM-42607 I2C driver" + depends on I2C + select INV_ICM42607 + select REGMAP_I2C + help + This driver supports the InvenSense ICM-42607 motion tracking + device over I2C. + + This driver can be built as a module. The module will be called + inv-icm42607-i2c. + +config INV_ICM42607_SPI + tristate "InvenSense ICM-42607 SPI driver" + depends on SPI_MASTER + select INV_ICM42607 + select REGMAP_SPI + help + This driver supports the InvenSense ICM-42607 motion tracking + device over SPI. + + This driver can be built as a module. The module will be called + inv-icm42607-spi. diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile new file mode 100644 index 000000000000..7b907e019601 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o +inv-icm42607-y += inv_icm42607_core.o +inv-icm42607-y += inv_icm42607_gyro.o +inv-icm42607-y += inv_icm42607_accel.o +inv-icm42607-y += inv_icm42607_temp.o + +obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o +inv-icm42607-i2c-y += inv_icm42607_i2c.o + +obj-$(CONFIG_INV_ICM42607_SPI) += inv-icm42607-spi.o +inv-icm42607-spi-y += inv_icm42607_spi.o diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h new file mode 100644 index 000000000000..4d51b0da1aa1 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#ifndef INV_ICM42607_H_ +#define INV_ICM42607_H_ + +#include <linux/bits.h> +#include <linux/iio/iio.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/time.h> +#include <linux/types.h> + +#include <asm/byteorder.h> + +/* + * Serial bus slew rates. Rates are expressed as range between the two + * values with the midpoint as the typical rate. For the final value of + * 2ns, 2ns is considered the max value with no expressed minimum or + * typical value. + */ +enum inv_icm42607_slew_rate { + INV_ICM42607_SLEW_RATE_20_60NS = 0, + INV_ICM42607_SLEW_RATE_12_36NS = 1, + INV_ICM42607_SLEW_RATE_6_19NS = 2, + INV_ICM42607_SLEW_RATE_4_14NS = 3, + INV_ICM42607_SLEW_RATE_2_6NS = 4, + INV_ICM42607_SLEW_RATE_2NS = 5, + INV_ICM42607_SLEW_RATE_NB +}; + +enum inv_icm42607_sensor_mode { + INV_ICM42607_SENSOR_MODE_OFF = 0, + INV_ICM42607_SENSOR_MODE_STANDBY = 1, + INV_ICM42607_SENSOR_MODE_LOW_POWER = 2, + INV_ICM42607_SENSOR_MODE_LOW_NOISE = 3, + INV_ICM42607_SENSOR_MODE_NB +}; + +/* gyroscope fullscale values */ +enum inv_icm42607_gyro_fs { + INV_ICM42607_GYRO_FS_2000DPS = 0, + INV_ICM42607_GYRO_FS_1000DPS = 1, + INV_ICM42607_GYRO_FS_500DPS = 2, + INV_ICM42607_GYRO_FS_250DPS = 3, + INV_ICM42607_GYRO_FS_NB +}; + +/* accelerometer fullscale values */ +enum inv_icm42607_accel_fs { + INV_ICM42607_ACCEL_FS_16G = 0, + INV_ICM42607_ACCEL_FS_8G = 1, + INV_ICM42607_ACCEL_FS_4G = 2, + INV_ICM42607_ACCEL_FS_2G = 3, + INV_ICM42607_ACCEL_FS_NB +}; + +/* ODR values - Note Gyro does not support ODR less than 12.5Hz */ +enum inv_icm42607_odr { + INV_ICM42607_ODR_1600HZ = 5, + INV_ICM42607_ODR_800HZ = 6, + INV_ICM42607_ODR_400HZ = 7, + INV_ICM42607_ODR_200HZ = 8, + INV_ICM42607_ODR_100HZ = 9, + INV_ICM42607_ODR_50HZ = 10, + INV_ICM42607_ODR_25HZ = 11, + INV_ICM42607_ODR_12_5HZ = 12, + INV_ICM42607_ODR_6_25HZ_LP = 13, + INV_ICM42607_ODR_3_125HZ_LP = 14, + INV_ICM42607_ODR_1_5625HZ_LP = 15, + INV_ICM42607_ODR_NB +}; + +/* Low-Noise mode sensor data filter (bandwidth) */ +enum inv_icm42607_filter_bw { + INV_ICM42607_FILTER_BYPASS = 0, + INV_ICM42607_FILTER_BW_180HZ = 1, + INV_ICM42607_FILTER_BW_121HZ = 2, + INV_ICM42607_FILTER_BW_73HZ = 3, + INV_ICM42607_FILTER_BW_53HZ = 4, + INV_ICM42607_FILTER_BW_34HZ = 5, + INV_ICM42607_FILTER_BW_25HZ = 6, + INV_ICM42607_FILTER_BW_16HZ = 7, + INV_ICM42607_FILTER_BW_NB +}; + +/* Low-Power mode sensor data filter (averaging) */ +enum inv_icm42607_filter_avg { + INV_ICM42607_FILTER_AVG_2X = 0, + INV_ICM42607_FILTER_AVG_4X = 1, + INV_ICM42607_FILTER_AVG_8X = 2, + INV_ICM42607_FILTER_AVG_16X = 3, + INV_ICM42607_FILTER_AVG_32X = 4, + INV_ICM42607_FILTER_AVG_64X = 5, + /* values 6 and 7 also correspond to 64x. */ +}; + +/* Temperature sensor data filter (bandwidth) */ +enum inv_icm42607_temp_filter_bw { + INV_ICM42607_TEMP_FILTER_BYPASS = 0, + INV_ICM42607_TEMP_FILTER_BW_180HZ = 1, + INV_ICM42607_TEMP_FILTER_BW_72HZ = 2, + INV_ICM42607_TEMP_FILTER_BW_34HZ = 3, + INV_ICM42607_TEMP_FILTER_BW_16HZ = 4, + INV_ICM42607_TEMP_FILTER_BW_8HZ = 5, + INV_ICM42607_TEMP_FILTER_BW_4HZ = 6, + /* value 7 also corresponds to 4Hz */ +}; + +/* Signed so that negative values can signify an invalid condition. */ +struct inv_icm42607_sensor_conf { + int mode; + int fs; + int odr; + int filter; +}; +#define INV_ICM42607_SENSOR_CONF_INIT { -1, -1, -1, -1 } + +struct inv_icm42607_conf { + struct inv_icm42607_sensor_conf gyro; + struct inv_icm42607_sensor_conf accel; + ktime_t gyro_stop; /* earliest time to stop the gyro */ +}; + +struct inv_icm42607_hw { + const char *name; + const struct inv_icm42607_conf *conf; + u8 whoami; +}; + +/** + * struct inv_icm42607_state - driver state variables + * @hw: Hardware specific data. + * @lock: lock for serializing multiple registers access. + * @map: regmap pointer. + * @indio_accel: accelerometer IIO device. + * @indio_gyro: gyroscope IIO device. + * @vddio_supply: I/O voltage regulator for the chip. + * @vddio_en: I/O voltage status for runtime PM. + * @conf: chip sensors configurations. + * @orientation: sensor chip orientation relative to main hardware. + */ +struct inv_icm42607_state { + const struct inv_icm42607_hw *hw; + struct mutex lock; + struct regmap *map; + struct iio_dev *indio_accel; + struct iio_dev *indio_gyro; + struct regulator *vddio_supply; + bool vddio_en; + struct inv_icm42607_conf conf; + struct iio_mount_matrix orientation; +}; + +/** + * struct inv_icm42607_sensor_state - sensor state variables + * @power_mode: sensor requested power mode (for common frequencies) + * @filter: sensor filter. + */ +struct inv_icm42607_sensor_state { + enum inv_icm42607_sensor_mode power_mode; + int filter; +}; + +/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */ + +/* Register Map for User Bank 0 */ +#define INV_ICM42607_REG_MCLK_RDY 0x00 + +#define INV_ICM42607_REG_DEVICE_CONFIG 0x01 +#define INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE BIT(2) +#define INV_ICM42607_DEVICE_CONFIG_SPI_MODE BIT(0) + +#define INV_ICM42607_REG_SIGNAL_PATH_RESET 0x02 +#define INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET BIT(4) +#define INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH BIT(2) + +#define INV_ICM42607_REG_DRIVE_CONFIG1 0x03 +#define INV_ICM42607_DRIVE_CONFIG1_I3C_DDR_MASK GENMASK(5, 3) +#define INV_ICM42607_DRIVE_CONFIG1_I3C_SDR_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_DRIVE_CONFIG2 0x04 +#define INV_ICM42607_DRIVE_CONFIG2_I2C_MASK GENMASK(5, 3) +#define INV_ICM42607_DRIVE_CONFIG2_ALL_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_DRIVE_CONFIG3 0x05 +#define INV_ICM42607_DRIVE_CONFIG3_SPI_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_INT_CONFIG 0x06 +#define INV_ICM42607_INT_CONFIG_INT2_LATCHED BIT(5) +#define INV_ICM42607_INT_CONFIG_INT2_PUSH_PULL BIT(4) +#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_HIGH BIT(3) +#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_LOW 0x00 +#define INV_ICM42607_INT_CONFIG_INT1_LATCHED BIT(2) +#define INV_ICM42607_INT_CONFIG_INT1_PUSH_PULL BIT(1) +#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_HIGH BIT(0) +#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_LOW 0x00 + +/* all sensor data are 16 bits (2 registers wide) in big-endian */ +#define INV_ICM42607_REG_TEMP_DATA1 0x09 +#define INV_ICM42607_REG_TEMP_DATA0 0x0A +#define INV_ICM42607_REG_ACCEL_DATA_X1 0x0B +#define INV_ICM42607_REG_ACCEL_DATA_X0 0x0C +#define INV_ICM42607_REG_ACCEL_DATA_Y1 0x0D +#define INV_ICM42607_REG_ACCEL_DATA_Y0 0x0E +#define INV_ICM42607_REG_ACCEL_DATA_Z1 0x0F +#define INV_ICM42607_REG_ACCEL_DATA_Z0 0x10 +#define INV_ICM42607_REG_GYRO_DATA_X1 0x11 +#define INV_ICM42607_REG_GYRO_DATA_X0 0x12 +#define INV_ICM42607_REG_GYRO_DATA_Y1 0x13 +#define INV_ICM42607_REG_GYRO_DATA_Y0 0x14 +#define INV_ICM42607_REG_GYRO_DATA_Z1 0x15 +#define INV_ICM42607_REG_GYRO_DATA_Z0 0x16 +#define INV_ICM42607_DATA_INVALID -32768 + +#define INV_ICM42607_REG_TMST_FSYNCH 0x17 +#define INV_ICM42607_REG_TMST_FSYNCL 0x18 + +/* APEX Data Registers */ +#define INV_ICM42607_REG_APEX_DATA0 0x31 +#define INV_ICM42607_REG_APEX_DATA1 0x32 +#define INV_ICM42607_REG_APEX_DATA2 0x33 +#define INV_ICM42607_REG_APEX_DATA3 0x34 +#define INV_ICM42607_REG_APEX_DATA4 0x1D +#define INV_ICM42607_REG_APEX_DATA5 0x1E + +#define INV_ICM42607_REG_PWR_MGMT0 0x1F +#define INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL BIT(7) +#define INV_ICM42607_PWR_MGMT0_IDLE BIT(4) +#define INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2) +#define INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0) + +#define INV_ICM42607_REG_GYRO_CONFIG0 0x20 +#define INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK GENMASK(6, 5) +#define INV_ICM42607_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0) + +#define INV_ICM42607_REG_ACCEL_CONFIG0 0x21 +#define INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK GENMASK(6, 5) +#define INV_ICM42607_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0) + +#define INV_ICM42607_REG_TEMP_CONFIG0 0x22 +#define INV_ICM42607_TEMP_CONFIG0_FILTER_MASK GENMASK(6, 4) + +#define INV_ICM42607_REG_GYRO_CONFIG1 0x23 +#define INV_ICM42607_GYRO_CONFIG1_FILTER_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_ACCEL_CONFIG1 0x24 +#define INV_ICM42607_ACCEL_CONFIG1_AVG_MASK GENMASK(6, 4) +#define INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_APEX_CONFIG0 0x25 +#define INV_ICM42607_APEX_CONFIG0_DMP_POWER_SAVE_EN BIT(3) +#define INV_ICM42607_APEX_CONFIG0_DMP_INIT_EN BIT(2) +#define INV_ICM42607_APEX_CONFIG0_DMP_MEM_RESET_EN BIT(0) + +#define INV_ICM42607_REG_APEX_CONFIG1 0x26 +#define INV_ICM42607_APEX_CONFIG1_SMD_ENABLE BIT(6) +#define INV_ICM42607_APEX_CONFIG1_FF_ENABLE BIT(5) +#define INV_ICM42607_APEX_CONFIG1_TILT_ENABLE BIT(4) +#define INV_ICM42607_APEX_CONFIG1_PED_ENABLE BIT(3) +#define INV_ICM42607_APEX_CONFIG1_DMP_ODR_MASK GENMASK(1, 0) + +#define INV_ICM42607_REG_WOM_CONFIG 0x27 +#define INV_ICM42607_WOM_CONFIG_INT_DUR_MASK GENMASK(4, 3) +#define INV_ICM42607_WOM_CONFIG_INT_MODE BIT(2) +#define INV_ICM42607_WOM_CONFIG_MODE BIT(1) +#define INV_ICM42607_WOM_CONFIG_EN BIT(0) + +#define INV_ICM42607_REG_FIFO_CONFIG1 0x28 +#define INV_ICM42607_FIFO_CONFIG1_MODE BIT(1) +#define INV_ICM42607_FIFO_CONFIG1_BYPASS BIT(0) + +#define INV_ICM42607_REG_FIFO_CONFIG2 0x29 +#define INV_ICM42607_REG_FIFO_CONFIG3 0x2A +#define INV_ICM42607_FIFO_WATERMARK_VAL(_wm) \ + cpu_to_le16((_wm) & GENMASK(11, 0)) +/* FIFO is 2048 bytes, let 12 samples for reading latency */ +#define INV_ICM42607_FIFO_WATERMARK_MAX (2048 - 12 * 16) +#define INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE 8 +#define INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE 16 + +#define INV_ICM42607_REG_INT_SOURCE0 0x2B +#define INV_ICM42607_INT_SOURCE0_ST_INT1_EN BIT(7) +#define INV_ICM42607_INT_SOURCE0_FSYNC_INT1_EN BIT(6) +#define INV_ICM42607_INT_SOURCE0_PLL_RDY_INT1_EN BIT(5) +#define INV_ICM42607_INT_SOURCE0_RESET_DONE_INT1_EN BIT(4) +#define INV_ICM42607_INT_SOURCE0_DRDY_INT1_EN BIT(3) +#define INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN BIT(2) +#define INV_ICM42607_INT_SOURCE0_FIFO_FULL_INT1_EN BIT(1) +#define INV_ICM42607_INT_SOURCE0_AGC_RDY_INT1_EN BIT(0) + +#define INV_ICM42607_REG_INT_SOURCE1 0x2C +#define INV_ICM42607_INT_SOURCE1_I3C_ERROR_INT1_EN BIT(6) +#define INV_ICM42607_INT_SOURCE1_SMD_INT1_EN BIT(3) +#define INV_ICM42607_INT_SOURCE1_WOM_INT1_EN GENMASK(2, 0) + +#define INV_ICM42607_REG_INT_SOURCE3 0x2D +#define INV_ICM42607_INT_SOURCE3_ST_INT2_EN BIT(7) +#define INV_ICM42607_INT_SOURCE3_FSYNC_INT2_EN BIT(6) +#define INV_ICM42607_INT_SOURCE3_PLL_RDY_INT2_EN BIT(5) +#define INV_ICM42607_INT_SOURCE3_RESET_DONE_INT2_EN BIT(4) +#define INV_ICM42607_INT_SOURCE3_DRDY_INT2_EN BIT(3) +#define INV_ICM42607_INT_SOURCE3_FIFO_THS_INT2_EN BIT(2) +#define INV_ICM42607_INT_SOURCE3_FIFO_FULL_INT2_EN BIT(1) +#define INV_ICM42607_INT_SOURCE3_AGC_RDY_INT2_EN BIT(0) + +#define INV_ICM42607_REG_INT_SOURCE4 0x2E +#define INV_ICM42607_INT_SOURCE4_I3C_ERROR_INT2_EN BIT(6) +#define INV_ICM42607_INT_SOURCE4_SMD_INT2_EN BIT(3) +#define INV_ICM42607_INT_SOURCE4_WOM_Z_INT2_EN BIT(2) +#define INV_ICM42607_INT_SOURCE4_WOM_Y_INT2_EN BIT(1) +#define INV_ICM42607_INT_SOURCE4_WOM_X_INT2_EN BIT(0) + +#define INV_ICM42607_REG_FIFO_LOST_PKT0 0x2F +#define INV_ICM42607_REG_FIFO_LOST_PKT1 0x30 + +#define INV_ICM42607_REG_INTF_CONFIG0 0x35 +#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_FORMAT BIT(6) +#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN BIT(5) +#define INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN BIT(4) +#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK GENMASK(1, 0) +#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS 2 +#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS 3 + +#define INV_ICM42607_REG_INTF_CONFIG1 0x36 +#define INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN BIT(3) +#define INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN BIT(2) +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK GENMASK(1, 0) +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_INT 0 +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL 1 +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_OFF 2 + +#define INV_ICM42607_REG_INT_STATUS_DRDY 0x39 +#define INV_ICM42607_INT_STATUS_DRDY_DATA_RDY BIT(0) + +#define INV_ICM42607_REG_INT_STATUS 0x3A +#define INV_ICM42607_INT_STATUS_ST BIT(7) +#define INV_ICM42607_INT_STATUS_FSYNC BIT(6) +#define INV_ICM42607_INT_STATUS_PLL_RDY BIT(5) +#define INV_ICM42607_INT_STATUS_RESET_DONE BIT(4) +#define INV_ICM42607_INT_STATUS_FIFO_THS BIT(2) +#define INV_ICM42607_INT_STATUS_FIFO_FULL BIT(1) +#define INV_ICM42607_INT_STATUS_AGC_RDY BIT(0) + +#define INV_ICM42607_REG_INT_STATUS2 0x3B +#define INV_ICM42607_INT_STATUS2_SMD BIT(3) +#define INV_ICM42607_INT_STATUS2_WOM_INT GENMASK(2, 0) + +#define INV_ICM42607_REG_INT_STATUS3 0x3C +#define INV_ICM42607_INT_STATUS3_STEP_DET BIT(5) +#define INV_ICM42607_INT_STATUS3_STEP_CNT_OVF BIT(4) +#define INV_ICM42607_INT_STATUS3_TILT_DET BIT(3) +#define INV_ICM42607_INT_STATUS3_FF_DET BIT(2) + +/* + * FIFO access registers + * FIFO count is 16 bits (2 registers) big-endian + * FIFO data is a continuous read register to read FIFO content + */ +#define INV_ICM42607_REG_FIFO_COUNTH 0x3D +#define INV_ICM42607_REG_FIFO_COUNTL 0x3E +#define INV_ICM42607_REG_FIFO_DATA 0x3F + +#define INV_ICM42607_REG_WHOAMI 0x75 +#define INV_ICM42607P_WHOAMI 0x60 +#define INV_ICM42607_WHOAMI 0x67 + +/* + * Timings as listed in section 3 of datasheet, all values listed in datasheet + * in ms except temp startup time... setting all values in us and using + * USEC_PER_MSEC to convert from values displayed in datasheet. + */ +#define INV_ICM42607_POWER_UP_TIME_US (100 * USEC_PER_MSEC) +#define INV_ICM42607_RESET_TIME_US (1 * USEC_PER_MSEC) +#define INV_ICM42607_ACCEL_STARTUP_TIME_US (10 * USEC_PER_MSEC) +#define INV_ICM42607_GYRO_STARTUP_TIME_US (30 * USEC_PER_MSEC) +#define INV_ICM42607_GYRO_STOP_TIME_US (45 * USEC_PER_MSEC) +#define INV_ICM42607_TEMP_STARTUP_TIME_US 77 + +/* + * Suspend delay assumed from other icm42600 series device, not + * documented in datasheet. + */ +#define INV_ICM42607_SUSPEND_DELAY_MS (2 * MSEC_PER_SEC) + +typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *); + +extern const struct regmap_config inv_icm42607_regmap_config; +extern const struct inv_icm42607_hw inv_icm42607_hw_data; +extern const struct inv_icm42607_hw inv_icm42607p_hw_data; +extern const struct dev_pm_ops inv_icm42607_pm_ops; + +const struct iio_mount_matrix * +inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan); + +int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st, + enum inv_icm42607_sensor_mode *gyro, + enum inv_icm42607_sensor_mode *accel); + +int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st, + struct inv_icm42607_sensor_conf *conf, + enum iio_chan_type chan_type); + +int inv_icm42607_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + s16 *val); + +int inv_icm42607_core_probe(struct regmap *regmap, + const struct inv_icm42607_hw *hw, + inv_icm42607_bus_setup bus_setup); + +struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st); + +struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st); + +#endif diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file mode 100644 index 000000000000..0b3f035c2da0 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/device/devres.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include "inv_icm42607.h" +#include "inv_icm42607_temp.h" + +#define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \ +{ \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = _modifier, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = 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 = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = _ext_info, \ +} + +enum inv_icm42607_accel_scan { + INV_ICM42607_ACCEL_SCAN_X, + INV_ICM42607_ACCEL_SCAN_Y, + INV_ICM42607_ACCEL_SCAN_Z, + INV_ICM42607_ACCEL_SCAN_TEMP, +}; + +static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix), + { } +}; + +static const struct iio_chan_spec inv_icm42607_accel_channels[] = { + INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42607_ACCEL_SCAN_X, + inv_icm42607_accel_ext_infos), + INV_ICM42607_ACCEL_CHAN(IIO_MOD_Y, INV_ICM42607_ACCEL_SCAN_Y, + inv_icm42607_accel_ext_infos), + INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z, + inv_icm42607_accel_ext_infos), + INV_ICM42607_TEMP_CHAN(INV_ICM42607_ACCEL_SCAN_TEMP), +}; + +static const int inv_icm42607_accel_scale_nano[][2] = { + [INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 }, + [INV_ICM42607_ACCEL_FS_8G] = { 0, 2394202 }, + [INV_ICM42607_ACCEL_FS_4G] = { 0, 1197101 }, + [INV_ICM42607_ACCEL_FS_2G] = { 0, 598550 }, +}; + +static int inv_icm42607_accel_read_scale(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + unsigned int idx; + + guard(mutex)(&st->lock); + + idx = st->conf.accel.fs; + + *val = inv_icm42607_accel_scale_nano[idx][0]; + *val2 = inv_icm42607_accel_scale_nano[idx][1]; + return IIO_VAL_INT_PLUS_NANO; +} + +static int inv_icm42607_accel_write_scale(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + size_t scales_len = ARRAY_SIZE(inv_icm42607_accel_scale_nano); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + int ret; + + for (idx = 0; idx < scales_len; idx++) { + if (val == inv_icm42607_accel_scale_nano[idx][0] && + val2 == inv_icm42607_accel_scale_nano[idx][1]) + break; + } + if (idx == scales_len) + return -EINVAL; + + conf.fs = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); +} + +/* IIO format int + micro , values 0-4 reserved. */ +static const int inv_icm42607_accel_odr[][2] = { + [INV_ICM42607_ODR_1600HZ] = { 1600, 0 }, + [INV_ICM42607_ODR_800HZ] = { 800, 0 }, + [INV_ICM42607_ODR_400HZ] = { 400, 0 }, + [INV_ICM42607_ODR_200HZ] = { 200, 0 }, + [INV_ICM42607_ODR_100HZ] = { 100, 0 }, + [INV_ICM42607_ODR_50HZ] = { 50, 0 }, + [INV_ICM42607_ODR_25HZ] = { 25, 0 }, + [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 }, + [INV_ICM42607_ODR_6_25HZ_LP] = { 6, 250000 }, + [INV_ICM42607_ODR_3_125HZ_LP] = { 3, 125000 }, + [INV_ICM42607_ODR_1_5625HZ_LP] = { 1, 562500 }, +}; + +static int inv_icm42607_accel_read_odr(struct inv_icm42607_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + guard(mutex)(&st->lock); + + odr = st->conf.accel.odr; + + for (i = INV_ICM42607_ODR_1600HZ; i < ARRAY_SIZE(inv_icm42607_accel_odr); i++) { + if (i == odr) + break; + } + if (i == ARRAY_SIZE(inv_icm42607_accel_odr)) + return -EINVAL; + + *val = inv_icm42607_accel_odr[i][0]; + *val2 = inv_icm42607_accel_odr[i][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + int ret; + + for (idx = INV_ICM42607_ODR_1600HZ; + idx < ARRAY_SIZE(inv_icm42607_accel_odr); idx++) { + if (val == inv_icm42607_accel_odr[idx][0] && + val2 == inv_icm42607_accel_odr[idx][1]) + break; + } + if (idx == ARRAY_SIZE(inv_icm42607_accel_odr)) + return -EINVAL; + + conf.odr = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); +} + +static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + s16 data; + int ret; + + switch (chan->type) { + case IIO_ACCEL: + break; + case IIO_TEMP: + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return inv_icm42607_temp_read_raw(indio_dev, chan, + val, val2, mask); + break; + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_read_sensor(indio_dev, chan, &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm42607_accel_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_accel_read_odr(st, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_accel_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: + if (chan->type != IIO_ACCEL) + return -EINVAL; + *vals = (const int *)inv_icm42607_accel_scale_nano; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(inv_icm42607_accel_scale_nano) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)inv_icm42607_accel_odr[INV_ICM42607_ODR_1600HZ]; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = (ARRAY_SIZE(inv_icm42607_accel_odr) - + INV_ICM42607_ODR_1600HZ) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int inv_icm42607_accel_write_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_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + ret = inv_icm42607_accel_write_scale(indio_dev, val, val2); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_accel_write_odr(indio_dev, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_accel_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info inv_icm42607_accel_info = { + .read_raw = inv_icm42607_accel_read_raw, + .read_avail = inv_icm42607_accel_read_avail, + .write_raw = inv_icm42607_accel_write_raw, + .write_raw_get_fmt = inv_icm42607_accel_write_raw_get_fmt, +}; + +struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st) +{ + struct device *dev = regmap_get_device(st->map); + struct inv_icm42607_sensor_state *accel_st; + struct iio_dev *indio_dev; + const char *name; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->hw->name); + if (!name) + return ERR_PTR(-ENOMEM); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st)); + if (!indio_dev) + return ERR_PTR(-ENOMEM); + + accel_st = iio_priv(indio_dev); + accel_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; + accel_st->filter = INV_ICM42607_FILTER_BW_73HZ; + + indio_dev->name = name; + indio_dev->info = &inv_icm42607_accel_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = inv_icm42607_accel_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_accel_channels); + iio_device_set_drvdata(indio_dev, st); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ERR_PTR(ret); + + return indio_dev; +} diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file mode 100644 index 000000000000..190e998f7b8e --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/ktime.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/time.h> +#include <linux/timekeeping.h> +#include <linux/types.h> + +#include <asm/byteorder.h> + +#include "inv_icm42607.h" + +static bool inv_icm42607_is_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INV_ICM42607_REG_MCLK_RDY ... INV_ICM42607_REG_INT_CONFIG: + case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_TMST_FSYNCL: + case INV_ICM42607_REG_APEX_DATA4 ... INV_ICM42607_REG_INTF_CONFIG1: + case INV_ICM42607_REG_INT_STATUS_DRDY ... INV_ICM42607_REG_FIFO_DATA: + case INV_ICM42607_REG_WHOAMI: + return true; + } + + return false; +} + +static bool inv_icm42607_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INV_ICM42607_REG_DEVICE_CONFIG ... INV_ICM42607_REG_INT_CONFIG: + case INV_ICM42607_REG_PWR_MGMT0 ... INV_ICM42607_REG_INT_SOURCE4: + case INV_ICM42607_REG_INTF_CONFIG0 ... INV_ICM42607_REG_INTF_CONFIG1: + return true; + } + + return false; +} + +static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INV_ICM42607_REG_MCLK_RDY: + case INV_ICM42607_REG_SIGNAL_PATH_RESET: + case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_APEX_DATA5: + case INV_ICM42607_REG_APEX_CONFIG0: + case INV_ICM42607_REG_FIFO_LOST_PKT0 ... INV_ICM42607_REG_APEX_DATA3: + case INV_ICM42607_REG_INT_STATUS_DRDY: + case INV_ICM42607_REG_INT_STATUS ... INV_ICM42607_REG_FIFO_DATA: + return true; + } + + return false; +} + +const struct regmap_config inv_icm42607_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = inv_icm42607_is_writeable_reg, + .readable_reg = inv_icm42607_is_readable_reg, + .volatile_reg = inv_icm42607_is_volatile_reg, + .max_register = INV_ICM42607_REG_WHOAMI, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607_regmap_config, "IIO_ICM42607"); + +/* chip initial default configuration */ +static const struct inv_icm42607_conf inv_icm42607_default_conf = { + .gyro = { + .mode = INV_ICM42607_SENSOR_MODE_OFF, + .fs = INV_ICM42607_GYRO_FS_1000DPS, + .odr = INV_ICM42607_ODR_100HZ, + .filter = INV_ICM42607_FILTER_BW_25HZ, + }, + .accel = { + .mode = INV_ICM42607_SENSOR_MODE_OFF, + .fs = INV_ICM42607_ACCEL_FS_4G, + .odr = INV_ICM42607_ODR_100HZ, + .filter = INV_ICM42607_FILTER_BW_25HZ, + }, +}; + +const struct inv_icm42607_hw inv_icm42607_hw_data = { + .whoami = INV_ICM42607_WHOAMI, + .name = "icm42607", + .conf = &inv_icm42607_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607_hw_data, "IIO_ICM42607"); + +const struct inv_icm42607_hw inv_icm42607p_hw_data = { + .whoami = INV_ICM42607P_WHOAMI, + .name = "icm42607p", + .conf = &inv_icm42607_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607"); + +const struct iio_mount_matrix * +inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + const struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + + return &st->orientation; +} + +static u32 inv_icm42607_odr_to_period_us(enum inv_icm42607_odr odr) +{ + static const u32 odr_periods[INV_ICM42607_ODR_NB] = { + /* Reserved values */ + 0, 0, 0, 0, 0, + /* 1600Hz */ + 625, + /* 800Hz */ + 1250, + /* 400Hz */ + 2500, + /* 200Hz */ + 5000, + /* 100 Hz */ + 10000, + /* 50Hz */ + 20000, + /* 25Hz */ + 40000, + /* 12.5Hz */ + 80000, + /* 6.25Hz */ + 160000, + /* 3.125Hz */ + 320000, + /* 1.5625Hz */ + 640000, + }; + + odr = clamp(odr, INV_ICM42607_ODR_1600HZ, INV_ICM42607_ODR_1_5625HZ_LP); + + return odr_periods[odr]; +} + +int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st, + enum inv_icm42607_sensor_mode *gyro, + enum inv_icm42607_sensor_mode *accel) +{ + unsigned int val; + int ret; + + ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val); + if (ret) + return ret; + + *gyro = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val); + *accel = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val); + + return 0; +} + +static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, + enum inv_icm42607_sensor_mode gyro, + enum inv_icm42607_sensor_mode accel) +{ + enum inv_icm42607_sensor_mode oldaccel, oldgyro; + unsigned int sleepval_us, odr_us; + unsigned int val; + s64 disable_wait; + int ret; + + ret = inv_icm42607_get_pwr_mgmt0(st, &oldgyro, &oldaccel); + if (ret) + return ret; + + if (gyro == oldgyro && accel == oldaccel) + return 0; + + /* + * Datasheet on page 14.26 says we need to ensure the gyro sensor is on + * for a minimum of 45ms. So if we transition from an on state to an + * off state make sure at least 45ms have passed before power off and + * wait if it hasn't. In case some platforms don't respond well to a + * sleep of 0, make sure the fsleep duration is > 0. + */ + if (!gyro && oldgyro) { + disable_wait = clamp(ktime_us_delta(st->conf.gyro_stop, ktime_get()), + 0, INV_ICM42607_GYRO_STOP_TIME_US); + + if (disable_wait > 0) + fsleep(disable_wait); + } + + val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, gyro) | + FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, accel); + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val); + if (ret) + return ret; + + /* + * If a state change occurs from off to on, sleep for the startup time + * of the sensor. Since more than one sensor can be transitioned from + * off to on, select the maximum time from each of the sensors changing + * from off to on. Also account for the time it takes for the first + * sample to be ready by looking up the odr value and adding it to the + * sleep time. The startup time for the temp sensor is considerably + * smaller than the startup time for the other sensors and one or more + * are required to be on for the temp sensor to function, so any start + * delay should be enough. + */ + sleepval_us = 0; + odr_us = 0; + if (accel && !oldaccel) { + sleepval_us = max(sleepval_us, INV_ICM42607_ACCEL_STARTUP_TIME_US); + odr_us = max(odr_us, inv_icm42607_odr_to_period_us(st->conf.accel.odr)); + } + + if (gyro && !oldgyro) { + sleepval_us = max(sleepval_us, INV_ICM42607_GYRO_STARTUP_TIME_US); + odr_us = max(odr_us, inv_icm42607_odr_to_period_us(st->conf.gyro.odr)); + /* Track the earliest we can turn off the gyroscope. */ + st->conf.gyro_stop = ktime_add_us(ktime_get(), + INV_ICM42607_GYRO_STOP_TIME_US); + } + + sleepval_us += odr_us; + + /* + * Only sleep if sleepval_us is greater than 0 in case some platforms + * have issues with a 0 delay. The 0 delay can happen if one or both + * sensors is shut down. + */ + if (sleepval_us > 0) + fsleep(sleepval_us); + + return 0; +} + +/* + * Sanity test between old and new config values, and note if we need + * to update register config0 or config1. + */ +static void inv_icm42607_update_config(struct inv_icm42607_sensor_conf *conf, + struct inv_icm42607_sensor_conf *oldconf, + bool *config0, bool *config1) +{ + if (conf->mode < 0) + conf->mode = oldconf->mode; + if (conf->fs < 0) + conf->fs = oldconf->fs; + if (conf->odr < 0) + conf->odr = oldconf->odr; + if (conf->filter < 0) + conf->filter = oldconf->filter; + + *config0 = (conf->fs != oldconf->fs) || (conf->odr != oldconf->odr); + + *config1 = (conf->filter != oldconf->filter); +} + +int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st, + struct inv_icm42607_sensor_conf *conf, + enum iio_chan_type chan_type) +{ + enum inv_icm42607_sensor_mode accel_mode, gyro_mode; + struct inv_icm42607_sensor_conf *oldconf; + bool config0, config1; + unsigned int val; + int ret; + + switch (chan_type) { + case IIO_ACCEL: + oldconf = &st->conf.accel; + break; + case IIO_ANGL_VEL: + oldconf = &st->conf.gyro; + break; + default: + return -EINVAL; + } + + /* + * Since we are only making one-shot calls today, we can avoid needless + * on/off cycles if we rely on the runtime power management to handle + * shutting off the sensors when not in use. The downside is if we + * enable both sensors then only read from one, we have to wait for + * runtime PM to turn it off. So just keep the mode of the sensor we + * are not actively using to whatever it's already set as. + */ + ret = inv_icm42607_get_pwr_mgmt0(st, &gyro_mode, &accel_mode); + if (ret) + return ret; + + inv_icm42607_update_config(conf, oldconf, &config0, &config1); + + if (config0) { + if (chan_type == IIO_ANGL_VEL) { + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->fs); + val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->odr); + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val); + } else { + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->fs); + val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->odr); + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val); + } + if (ret) + return ret; + + oldconf->fs = conf->fs; + oldconf->odr = conf->odr; + } + + if (config1) { + if (chan_type == IIO_ANGL_VEL) { + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, + conf->filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1, + INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val); + } else { + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, + conf->filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, + INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val); + } + if (ret) + return ret; + + oldconf->filter = conf->filter; + } + + oldconf->mode = conf->mode; + + switch (chan_type) { + case IIO_ACCEL: + return inv_icm42607_set_pwr_mgmt0(st, gyro_mode, conf->mode); + case IIO_ANGL_VEL: + return inv_icm42607_set_pwr_mgmt0(st, conf->mode, accel_mode); + default: + return -EINVAL; + } +} + +int inv_icm42607_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + s16 *val) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42607_sensor_state *sensor_st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int reg; + __be16 data; + int ret; + + if ((chan->type != IIO_ANGL_VEL) && (chan->type != IIO_ACCEL)) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + if (chan->type == IIO_ANGL_VEL) + reg = INV_ICM42607_REG_GYRO_DATA_X1; + else + reg = INV_ICM42607_REG_ACCEL_DATA_X1; + break; + case IIO_MOD_Y: + if (chan->type == IIO_ANGL_VEL) + reg = INV_ICM42607_REG_GYRO_DATA_Y1; + else + reg = INV_ICM42607_REG_ACCEL_DATA_Y1; + break; + case IIO_MOD_Z: + if (chan->type == IIO_ANGL_VEL) + reg = INV_ICM42607_REG_GYRO_DATA_Z1; + else + reg = INV_ICM42607_REG_ACCEL_DATA_Z1; + break; + default: + return -EINVAL; + } + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + /* enable sensor */ + conf.mode = sensor_st->power_mode; + conf.filter = sensor_st->filter; + ret = inv_icm42607_set_sensor_conf(st, &conf, chan->type); + if (ret) + return ret; + + /* read sensor register data */ + ret = regmap_bulk_read(st->map, reg, &data, sizeof(data)); + if (ret) + return ret; + + *val = be16_to_cpu(data); + if (*val == INV_ICM42607_DATA_INVALID) + return -EINVAL; + + return 0; +} + +static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st, + const struct inv_icm42607_conf *conf) +{ + unsigned int val; + int ret; + + val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode); + val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode); + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->gyro.fs); + val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr); + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->accel.fs); + val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr); + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, conf->gyro.filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1, + INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, conf->accel.filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, + INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, + INV_ICM42607_TEMP_FILTER_BW_34HZ); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_TEMP_CONFIG0, + INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, val); + if (ret) + return ret; + + st->conf = *conf; + + return 0; +} + +static int inv_icm42607_setup(struct inv_icm42607_state *st, + inv_icm42607_bus_setup inv_icm42607_bus_setup) +{ + const struct device *dev = regmap_get_device(st->map); + unsigned int val; + int ret; + + ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val); + if (ret) + return ret; + + /* Warn, but don't fail. */ + if (val != st->hw->whoami) + dev_warn(dev, "Unknown whoami %#02x expected %#02x (%s)\n", + val, st->hw->whoami, st->hw->name); + + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET, + INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET); + if (ret) + return ret; + + fsleep(1 * USEC_PER_MSEC); + + /* + * No polling interval specified in datasheet, so use reset time as + * polling interval and 10x reset time as timeout period. + */ + ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS, + val, val & INV_ICM42607_INT_STATUS_RESET_DONE, + 1 * USEC_PER_MSEC, 10 * USEC_PER_MSEC); + if (ret) + return dev_err_probe(dev, ret, + "reset error, reset done bit not set\n"); + + /* Sync the regcache again after a reset. */ + regcache_mark_dirty(st->map); + ret = regcache_sync(st->map); + if (ret) + return ret; + + ret = inv_icm42607_bus_setup(st); + if (ret) + return ret; + + ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, + INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, + val); + if (ret) + return ret; + + return inv_icm42607_set_init_conf(st, st->hw->conf); +} + +static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st) +{ + int ret; + + if (st->vddio_en) + return 0; + + ret = regulator_enable(st->vddio_supply); + if (ret) + return ret; + + fsleep(INV_ICM42607_POWER_UP_TIME_US); + + st->vddio_en = true; + + return 0; +} + +static void inv_icm42607_sensors_off(void *_data) +{ + struct inv_icm42607_state *st = _data; + const struct device *dev = regmap_get_device(st->map); + int ret; + + guard(mutex)(&st->lock); + + st->conf.gyro.mode = INV_ICM42607_SENSOR_MODE_OFF; + st->conf.accel.mode = INV_ICM42607_SENSOR_MODE_OFF; + + ret = inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode, + st->conf.accel.mode); + if (ret) + dev_err(dev, "Unable to turn off sensors\n"); +} + +static void inv_icm42607_disable_vddio_reg(void *_data) +{ + struct inv_icm42607_state *st = _data; + + if (!st->vddio_en) + return; + + regulator_disable(st->vddio_supply); + + st->vddio_en = false; +} + +int inv_icm42607_core_probe(struct regmap *regmap, + const struct inv_icm42607_hw *hw, + inv_icm42607_bus_setup inv_icm42607_bus_setup) +{ + struct device *dev = regmap_get_device(regmap); + struct inv_icm42607_state *st; + int ret; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + dev_set_drvdata(dev, st); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->hw = hw; + st->map = regmap; + + ret = iio_read_mount_matrix(dev, &st->orientation); + if (ret) + return dev_err_probe(dev, ret, + "failed to retrieve mounting matrix\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get vdd regulator\n"); + + st->vddio_supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(st->vddio_supply)) + return dev_err_probe(dev, PTR_ERR(st->vddio_supply), + "Failed to get vddio regulator\n"); + + ret = inv_icm42607_enable_vddio_reg(st); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, inv_icm42607_disable_vddio_reg, st); + if (ret) + return ret; + + /* Setup chip registers (includes WHOAMI check, reset check, bus setup) */ + ret = inv_icm42607_setup(st, inv_icm42607_bus_setup); + if (ret) + return ret; + + /* + * Ensure if sensors get turned on at some point, they're turned off + * as part of teardown. + */ + ret = devm_add_action_or_reset(dev, inv_icm42607_sensors_off, st); + if (ret) + return ret; + + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + /* Initialize IIO device for Accel */ + st->indio_accel = inv_icm42607_accel_init(st); + if (IS_ERR(st->indio_accel)) + return PTR_ERR(st->indio_accel); + + /* Initialize IIO device for Gyro */ + st->indio_gyro = inv_icm42607_gyro_init(st); + if (IS_ERR(st->indio_gyro)) + return PTR_ERR(st->indio_gyro); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607"); + +static int inv_icm42607_suspend(struct device *dev) +{ + struct inv_icm42607_state *st = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + inv_icm42607_disable_vddio_reg(st); + + return 0; +} + +static int inv_icm42607_resume(struct device *dev) +{ + struct inv_icm42607_state *st = dev_get_drvdata(dev); + int ret; + + ret = inv_icm42607_enable_vddio_reg(st); + if (ret) + return ret; + + /* Sync the regcache again after regulator shutdown. */ + regcache_mark_dirty(st->map); + ret = regcache_sync(st->map); + if (ret) + return ret; + + return pm_runtime_force_resume(dev); +} + +static int inv_icm42607_runtime_suspend(struct device *dev) +{ + struct inv_icm42607_state *st = dev_get_drvdata(dev); + + /* + * Set sensors state to off. Since we only support one-shot + * today we can use runtime PM to turn sensors off when not + * in use, and then when needed the reads/writes will + * re-enable the sensors as needed. This reduces complexity, + * however the tradeoff is that an unused sensor won't be + * turned off until the entire chip is no longer in use. + */ + inv_icm42607_sensors_off(st); + return 0; +} + +EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42607_pm_ops, IIO_ICM42607) = { + SYSTEM_SLEEP_PM_OPS(inv_icm42607_suspend, inv_icm42607_resume) + RUNTIME_PM_OPS(inv_icm42607_runtime_suspend, NULL, NULL) +}; + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-42607 device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c new file mode 100644 index 000000000000..5b4683c2dd1e --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/device/devres.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include "inv_icm42607.h" +#include "inv_icm42607_temp.h" + +#define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \ +{ \ + .type = IIO_ANGL_VEL, \ + .modified = 1, \ + .channel2 = _modifier, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = 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 = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = _ext_info, \ +} + +enum inv_icm42607_gyro_scan { + INV_ICM42607_GYRO_SCAN_X, + INV_ICM42607_GYRO_SCAN_Y, + INV_ICM42607_GYRO_SCAN_Z, + INV_ICM42607_GYRO_SCAN_TEMP, +}; + +static const struct iio_chan_spec_ext_info inv_icm42607_gyro_ext_infos[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix), + { } +}; + +static const struct iio_chan_spec inv_icm42607_gyro_channels[] = { + INV_ICM42607_GYRO_CHAN(IIO_MOD_X, INV_ICM42607_GYRO_SCAN_X, + inv_icm42607_gyro_ext_infos), + INV_ICM42607_GYRO_CHAN(IIO_MOD_Y, INV_ICM42607_GYRO_SCAN_Y, + inv_icm42607_gyro_ext_infos), + INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42607_GYRO_SCAN_Z, + inv_icm42607_gyro_ext_infos), + INV_ICM42607_TEMP_CHAN(INV_ICM42607_GYRO_SCAN_TEMP), +}; + +static const int inv_icm42607_gyro_scale_nano[][2] = { + [INV_ICM42607_GYRO_FS_2000DPS] = { 0, 1065264 }, + [INV_ICM42607_GYRO_FS_1000DPS] = { 0, 532632 }, + [INV_ICM42607_GYRO_FS_500DPS] = { 0, 266316 }, + [INV_ICM42607_GYRO_FS_250DPS] = { 0, 133158 }, +}; + +static int inv_icm42607_gyro_read_scale(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + unsigned int idx; + + guard(mutex)(&st->lock); + + idx = st->conf.gyro.fs; + + *val = inv_icm42607_gyro_scale_nano[idx][0]; + *val2 = inv_icm42607_gyro_scale_nano[idx][1]; + return IIO_VAL_INT_PLUS_NANO; +} + +static int inv_icm42607_gyro_write_scale(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + size_t scales_len = ARRAY_SIZE(inv_icm42607_gyro_scale_nano); + int ret; + + for (idx = 0; idx < scales_len; idx++) { + if (val == inv_icm42607_gyro_scale_nano[idx][0] && + val2 == inv_icm42607_gyro_scale_nano[idx][1]) + break; + } + if (idx == scales_len) + return -EINVAL; + + conf.fs = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL); +} + +/* IIO format int + micro , values 0-4 reserved. */ +static const int inv_icm42607_gyro_odr[][2] = { + [INV_ICM42607_ODR_1600HZ] = { 1600, 0 }, + [INV_ICM42607_ODR_800HZ] = { 800, 0 }, + [INV_ICM42607_ODR_400HZ] = { 400, 0 }, + [INV_ICM42607_ODR_200HZ] = { 200, 0 }, + [INV_ICM42607_ODR_100HZ] = { 100, 0 }, + [INV_ICM42607_ODR_50HZ] = { 50, 0 }, + [INV_ICM42607_ODR_25HZ] = { 25, 0 }, + [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 }, +}; + +static int inv_icm42607_gyro_read_odr(struct inv_icm42607_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + guard(mutex)(&st->lock); + + odr = st->conf.gyro.odr; + + for (i = INV_ICM42607_ODR_1600HZ; i < ARRAY_SIZE(inv_icm42607_gyro_odr); i++) { + if (i == odr) + break; + } + if (i == ARRAY_SIZE(inv_icm42607_gyro_odr)) + return -EINVAL; + + *val = inv_icm42607_gyro_odr[i][0]; + *val2 = inv_icm42607_gyro_odr[i][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int inv_icm42607_gyro_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + int ret; + + for (idx = INV_ICM42607_ODR_1600HZ; + idx < ARRAY_SIZE(inv_icm42607_gyro_odr); idx++) { + if (val == inv_icm42607_gyro_odr[idx][0] && + val2 == inv_icm42607_gyro_odr[idx][1]) + break; + } + if (idx == ARRAY_SIZE(inv_icm42607_gyro_odr)) + return -EINVAL; + + conf.odr = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL); +} + +static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + s16 data; + int ret; + + switch (chan->type) { + case IIO_ANGL_VEL: + break; + case IIO_TEMP: + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return inv_icm42607_temp_read_raw(indio_dev, chan, + val, val2, mask); + break; + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_read_sensor(indio_dev, chan, &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm42607_gyro_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_gyro_read_odr(st, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_gyro_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: + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + *vals = (const int *)inv_icm42607_gyro_scale_nano; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(inv_icm42607_gyro_scale_nano) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)inv_icm42607_gyro_odr[INV_ICM42607_ODR_1600HZ]; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = (ARRAY_SIZE(inv_icm42607_gyro_odr) - + INV_ICM42607_ODR_1600HZ) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int inv_icm42607_gyro_write_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_SCALE: + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + ret = inv_icm42607_gyro_write_scale(indio_dev, val, val2); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_gyro_write_odr(indio_dev, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_gyro_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info inv_icm42607_gyro_info = { + .read_raw = inv_icm42607_gyro_read_raw, + .read_avail = inv_icm42607_gyro_read_avail, + .write_raw = inv_icm42607_gyro_write_raw, + .write_raw_get_fmt = inv_icm42607_gyro_write_raw_get_fmt, +}; + +struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st) +{ + struct device *dev = regmap_get_device(st->map); + const char *name; + struct inv_icm42607_sensor_state *gyro_st; + struct iio_dev *indio_dev; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->hw->name); + if (!name) + return ERR_PTR(-ENOMEM); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st)); + if (!indio_dev) + return ERR_PTR(-ENOMEM); + + gyro_st = iio_priv(indio_dev); + gyro_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; + gyro_st->filter = INV_ICM42607_FILTER_BW_73HZ; + + indio_dev->name = name; + indio_dev->info = &inv_icm42607_gyro_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = inv_icm42607_gyro_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_gyro_channels); + iio_device_set_drvdata(indio_dev, st); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ERR_PTR(ret); + + return indio_dev; +} diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file mode 100644 index 000000000000..e903106af84a --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include <linux/bitfield.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "inv_icm42607.h" + +static int inv_icm42607_i2c_bus_setup(struct inv_icm42607_state *st) +{ + unsigned int val; + int ret; + + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN | + INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, + INV_ICM42607_SLEW_RATE_12_36NS); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG2, + INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS); + + return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + val); +} + +static int inv_icm42607_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct inv_icm42607_hw *hw; + struct regmap *regmap; + + hw = i2c_get_match_data(client); + if (!hw) + return dev_err_probe(dev, -ENODEV, "Failed to get i2c data\n"); + + regmap = devm_regmap_init_i2c(client, &inv_icm42607_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to register i2c regmap\n"); + + return inv_icm42607_core_probe(regmap, hw, inv_icm42607_i2c_bus_setup); +} + +static const struct i2c_device_id inv_icm42607_id[] = { + { + .name = "icm42607", + .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data, + }, { + .name = "icm42607p", + .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(i2c, inv_icm42607_id); + +static const struct of_device_id inv_icm42607_of_matches[] = { + { + .compatible = "invensense,icm42607", + .data = &inv_icm42607_hw_data, + }, { + .compatible = "invensense,icm42607p", + .data = &inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches); + +static struct i2c_driver inv_icm42607_driver = { + .driver = { + .name = "inv-icm42607-i2c", + .of_match_table = inv_icm42607_of_matches, + .pm = pm_ptr(&inv_icm42607_pm_ops), + }, + .id_table = inv_icm42607_id, + .probe = inv_icm42607_probe, +}; +module_i2c_driver(inv_icm42607_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-42607 I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM42607"); diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file mode 100644 index 000000000000..cd739375f6b2 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include <linux/bitfield.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "inv_icm42607.h" + +static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st) +{ + unsigned int val; + int ret; + + /* Only support 4-wire mode for now. */ + ret = regmap_set_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG, + INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE); + if (ret) + return ret; + + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN | + INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, + INV_ICM42607_SLEW_RATE_2NS); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3, + INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS); + + return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + val); +} + +static int inv_icm42607_probe(struct spi_device *spi) +{ + const struct inv_icm42607_hw *hw; + struct device *dev = &spi->dev; + struct regmap *regmap; + + hw = spi_get_device_match_data(spi); + if (!hw) + return dev_err_probe(dev, -ENODATA, "Failed to get SPI data\n"); + + if (spi->mode & SPI_3WIRE) + return dev_err_probe(dev, -ENODEV, "SPI 3-wire mode not supported\n"); + + regmap = devm_regmap_init_spi(spi, &inv_icm42607_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to register spi regmap\n"); + + return inv_icm42607_core_probe(regmap, hw, inv_icm42607_spi_bus_setup); +} + +static const struct spi_device_id inv_icm42607_spi_id_table[] = { + { + .name = "icm42607", + .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data, + }, { + .name = "icm42607p", + .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, inv_icm42607_spi_id_table); + +static const struct of_device_id inv_icm42607_of_matches[] = { + { + .compatible = "invensense,icm42607", + .data = &inv_icm42607_hw_data, + }, + { + .compatible = "invensense,icm42607p", + .data = &inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches); + +static struct spi_driver inv_icm42607_driver = { + .driver = { + .name = "inv-icm42607-spi", + .of_match_table = inv_icm42607_of_matches, + .pm = pm_ptr(&inv_icm42607_pm_ops), + }, + .id_table = inv_icm42607_spi_id_table, + .probe = inv_icm42607_probe, +}; +module_spi_driver(inv_icm42607_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-42607 SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM42607"); diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file mode 100644 index 000000000000..53f0484c0845 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/types.h> +#include <linux/unaligned.h> + +#include "inv_icm42607.h" +#include "inv_icm42607_temp.h" + +static int inv_icm42607_temp_read(struct inv_icm42607_state *st, s16 *temp) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct device *dev = regmap_get_device(st->map); + int ret, gyro_mode, accel_mode; + unsigned int val; + u8 raw[2]; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + /* + * Check if both the gyro and accel are off and if so, enable one + * of them. The temp sensor cannot be read if both the gyro and + * accel sensor are off. Prefer to enable the accel over the gyro + * as the datasheet says the gyro uses 5x more power and it has + * a minimum run time of 45ms. + */ + ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val); + if (ret) + return ret; + + accel_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val); + gyro_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val); + if (!gyro_mode && !accel_mode) { + /* enable accel sensor */ + conf.mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; + ret = inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); + if (ret) + return ret; + } + + ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1, + raw, sizeof(raw)); + if (ret) + return ret; + + *temp = get_unaligned_be16(raw); + if (*temp == INV_ICM42607_DATA_INVALID) + return -EINVAL; + + return 0; +} + +int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + s16 temp; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_temp_read(st, &temp); + if (ret) + return ret; + *val = temp; + return IIO_VAL_INT; + /* + * T°C = (temp / 128) + 25 + * Tm°C = 1000 * ((temp * 100 / 12800) + 25) + * scale: 100000 / 12800 ~= 7.8125 + * offset: 3200 + */ + case IIO_CHAN_INFO_SCALE: + *val = 7; + *val2 = 812500000; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + *val = 3200; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h new file mode 100644 index 000000000000..18499b4d0b94 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#ifndef INV_ICM42607_TEMP_H_ +#define INV_ICM42607_TEMP_H_ + +#include <linux/bitops.h> + +struct iio_dev; +struct iio_chan_spec; + +#define INV_ICM42607_TEMP_CHAN(_index) \ +{ \ + .type = IIO_TEMP, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + 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 = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask); + +#endif diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h index 1c796d4b2a40..95fa934a42b4 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -190,7 +190,7 @@ struct inv_icm45600_sensor_state { #define INV_ICM45600_REG_IREG_ADDR 0x7C #define INV_ICM45600_REG_IREG_DATA 0x7E -/* Direct acces registers */ +/* Direct access registers */ #define INV_ICM45600_REG_MISC2 0x007F #define INV_ICM45600_MISC2_SOFT_RESET BIT(1) diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c index 2b9ea317385c..42111c543d3c 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c @@ -422,8 +422,11 @@ int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st, if (max > 0 && fifo_nb > max) fifo_nb = max; - /* Try to read all FIFO data in internal buffer. */ - st->fifo.count = fifo_nb * packet_size; + /* + * Read all FIFO data into the internal buffer, clamping the + * device-reported count to the buffer capacity. + */ + st->fifo.count = min(fifo_nb * packet_size, INV_ICM45600_FIFO_SIZE_MAX); ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA, st->fifo.data, st->fifo.count); if (ret == -ENOTSUPP || ret == -EFBIG) { diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c index d49053161a65..c1d7aa7e950d 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -716,7 +716,7 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chi dev_set_drvdata(dev, st); - st->fifo.data = devm_kzalloc(dev, 8192, GFP_KERNEL); + st->fifo.data = devm_kzalloc(dev, INV_ICM45600_FIFO_SIZE_MAX, GFP_KERNEL); if (!st->fifo.data) return -ENOMEM; diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c index 81ba1b60f04b..69acf9bac2db 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c @@ -22,7 +22,7 @@ static int inv_icm45600_probe(struct i2c_client *client) if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -ENODEV; - chip_info = device_get_match_data(&client->dev); + chip_info = i2c_get_match_data(client); if (!chip_info) return -ENODEV; diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c index 450a0f2abaaa..087e590e04c2 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c @@ -48,14 +48,14 @@ static int inv_icm45600_probe(struct spi_device *spi) * supported by this driver. */ static const struct spi_device_id inv_icm45600_id[] = { - { "icm45605", (kernel_ulong_t)&inv_icm45605_chip_info }, - { "icm45606", (kernel_ulong_t)&inv_icm45606_chip_info }, - { "icm45608", (kernel_ulong_t)&inv_icm45608_chip_info }, - { "icm45634", (kernel_ulong_t)&inv_icm45634_chip_info }, - { "icm45686", (kernel_ulong_t)&inv_icm45686_chip_info }, - { "icm45687", (kernel_ulong_t)&inv_icm45687_chip_info }, - { "icm45688p", (kernel_ulong_t)&inv_icm45688p_chip_info }, - { "icm45689", (kernel_ulong_t)&inv_icm45689_chip_info }, + { .name = "icm45605", .driver_data = (kernel_ulong_t)&inv_icm45605_chip_info }, + { .name = "icm45606", .driver_data = (kernel_ulong_t)&inv_icm45606_chip_info }, + { .name = "icm45608", .driver_data = (kernel_ulong_t)&inv_icm45608_chip_info }, + { .name = "icm45634", .driver_data = (kernel_ulong_t)&inv_icm45634_chip_info }, + { .name = "icm45686", .driver_data = (kernel_ulong_t)&inv_icm45686_chip_info }, + { .name = "icm45687", .driver_data = (kernel_ulong_t)&inv_icm45687_chip_info }, + { .name = "icm45688p", .driver_data = (kernel_ulong_t)&inv_icm45688p_chip_info }, + { .name = "icm45689", .driver_data = (kernel_ulong_t)&inv_icm45689_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, inv_icm45600_id); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 47394594d17a..6b858fdfd1c6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -106,9 +106,9 @@ static int inv_magn_init(struct inv_mpu6050_state *st) return ret; /* - * Sensor sentivity + * Sensor sensitivity * 1 uT = 0.01 G and value is in micron (1e6) - * sensitvity = x uT * 0.01 * 1e6 + * sensitivity = x uT * 0.01 * 1e6 */ switch (st->chip_type) { case INV_MPU9150: diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index b8204eb0b4c6..71e90ddc19dd 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -65,23 +65,23 @@ static int inv_mpu_probe(struct spi_device *spi) * supported by this driver */ static const struct spi_device_id inv_mpu_id[] = { - {"mpu6000", INV_MPU6000}, - {"mpu6500", INV_MPU6500}, - {"mpu6515", INV_MPU6515}, - {"mpu6880", INV_MPU6880}, - {"mpu9250", INV_MPU9250}, - {"mpu9255", INV_MPU9255}, - {"icm20608", INV_ICM20608}, - {"icm20608d", INV_ICM20608D}, - {"icm20609", INV_ICM20609}, - {"icm20689", INV_ICM20689}, - {"icm20600", INV_ICM20600}, - {"icm20602", INV_ICM20602}, - {"icm20690", INV_ICM20690}, - {"iam20380", INV_IAM20380}, - {"iam20680", INV_IAM20680}, - {"iam20680hp", INV_IAM20680HP}, - {"iam20680ht", INV_IAM20680HT}, + { .name = "mpu6000", .driver_data = INV_MPU6000 }, + { .name = "mpu6500", .driver_data = INV_MPU6500 }, + { .name = "mpu6515", .driver_data = INV_MPU6515 }, + { .name = "mpu6880", .driver_data = INV_MPU6880 }, + { .name = "mpu9250", .driver_data = INV_MPU9250 }, + { .name = "mpu9255", .driver_data = INV_MPU9255 }, + { .name = "icm20608", .driver_data = INV_ICM20608 }, + { .name = "icm20608d", .driver_data = INV_ICM20608D }, + { .name = "icm20609", .driver_data = INV_ICM20609 }, + { .name = "icm20689", .driver_data = INV_ICM20689 }, + { .name = "icm20600", .driver_data = INV_ICM20600 }, + { .name = "icm20602", .driver_data = INV_ICM20602 }, + { .name = "icm20690", .driver_data = INV_ICM20690 }, + { .name = "iam20380", .driver_data = INV_IAM20380 }, + { .name = "iam20680", .driver_data = INV_IAM20680 }, + { .name = "iam20680hp", .driver_data = INV_IAM20680HP }, + { .name = "iam20680ht", .driver_data = INV_IAM20680HT }, { } }; diff --git a/drivers/iio/imu/smi240.c b/drivers/iio/imu/smi240.c index d159ee59acdd..f419296bba88 100644 --- a/drivers/iio/imu/smi240.c +++ b/drivers/iio/imu/smi240.c @@ -593,7 +593,7 @@ static int smi240_probe(struct spi_device *spi) } static const struct spi_device_id smi240_spi_id[] = { - { "smi240" }, + { .name = "smi240" }, { } }; MODULE_DEVICE_TABLE(spi, smi240_spi_id); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index f4edcb73ec8c..360f1365f892 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -2852,11 +2852,8 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) st_lsm6dsx_handler_thread, irq_type | IRQF_ONESHOT, "lsm6dsx", hw); - if (err) { - dev_err(hw->dev, "failed to request trigger irq %d\n", - hw->irq); + if (err) return err; - } return 0; } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c index 2e72c7ba94dd..5321033daebc 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c @@ -112,6 +112,11 @@ int st_lsm6dsx_fusion_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) return err; } + if (enable) + hw->enable_mask |= BIT(ST_LSM6DSX_ID_FUSION); + else + hw->enable_mask &= ~BIT(ST_LSM6DSX_ID_FUSION); + return st_lsm6dsx_fusion_page_disable(hw); } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 3389b15df0bc..ed47d5a6f11c 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -138,30 +138,30 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { - { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, - { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, - { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, - { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, - { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, - { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, - { ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID }, - { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID }, - { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID }, - { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, - { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, - { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, - { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, - { ST_LSM6DSRX_DEV_NAME, ST_LSM6DSRX_ID }, - { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, - { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, - { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, - { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, - { ST_LSM6DSV_DEV_NAME, ST_LSM6DSV_ID }, - { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, - { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, - { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, - { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, - { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, + { .name = ST_LSM6DS3_DEV_NAME, .driver_data = ST_LSM6DS3_ID }, + { .name = ST_LSM6DS3H_DEV_NAME, .driver_data = ST_LSM6DS3H_ID }, + { .name = ST_LSM6DSL_DEV_NAME, .driver_data = ST_LSM6DSL_ID }, + { .name = ST_LSM6DSM_DEV_NAME, .driver_data = ST_LSM6DSM_ID }, + { .name = ST_ISM330DLC_DEV_NAME, .driver_data = ST_ISM330DLC_ID }, + { .name = ST_LSM6DSO_DEV_NAME, .driver_data = ST_LSM6DSO_ID }, + { .name = ST_ASM330LHH_DEV_NAME, .driver_data = ST_ASM330LHH_ID }, + { .name = ST_LSM6DSOX_DEV_NAME, .driver_data = ST_LSM6DSOX_ID }, + { .name = ST_LSM6DSR_DEV_NAME, .driver_data = ST_LSM6DSR_ID }, + { .name = ST_LSM6DS3TRC_DEV_NAME, .driver_data = ST_LSM6DS3TRC_ID }, + { .name = ST_ISM330DHCX_DEV_NAME, .driver_data = ST_ISM330DHCX_ID }, + { .name = ST_LSM9DS1_DEV_NAME, .driver_data = ST_LSM9DS1_ID }, + { .name = ST_LSM6DS0_DEV_NAME, .driver_data = ST_LSM6DS0_ID }, + { .name = ST_LSM6DSRX_DEV_NAME, .driver_data = ST_LSM6DSRX_ID }, + { .name = ST_LSM6DST_DEV_NAME, .driver_data = ST_LSM6DST_ID }, + { .name = ST_LSM6DSOP_DEV_NAME, .driver_data = ST_LSM6DSOP_ID }, + { .name = ST_ASM330LHHX_DEV_NAME, .driver_data = ST_ASM330LHHX_ID }, + { .name = ST_LSM6DSTX_DEV_NAME, .driver_data = ST_LSM6DSTX_ID }, + { .name = ST_LSM6DSV_DEV_NAME, .driver_data = ST_LSM6DSV_ID }, + { .name = ST_LSM6DSV16X_DEV_NAME, .driver_data = ST_LSM6DSV16X_ID }, + { .name = ST_LSM6DSO16IS_DEV_NAME, .driver_data = ST_LSM6DSO16IS_ID }, + { .name = ST_ISM330IS_DEV_NAME, .driver_data = ST_ISM330IS_ID }, + { .name = ST_ASM330LHB_DEV_NAME, .driver_data = ST_ASM330LHB_ID }, + { .name = ST_ASM330LHHXG1_DEV_NAME, .driver_data = ST_ASM330LHHXG1_ID }, { } }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c index 8c5d8535e54c..e67f09e1f093 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c @@ -32,8 +32,8 @@ static const struct of_device_id st_lsm9ds0_of_match[] = { MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); static const struct spi_device_id st_lsm9ds0_id_table[] = { - { LSM303D_IMU_DEV_NAME }, - { LSM9DS0_IMU_DEV_NAME }, + { .name = LSM303D_IMU_DEV_NAME }, + { .name = LSM9DS0_IMU_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_lsm9ds0_id_table); diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 4763e224ebc6..f7a4be8ec320 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -649,7 +649,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, "IIO_BACKEND"); * @len: Buffer length * * This helper is intended to be used by backends that extend an IIO channel - * (trough iio_backend_extend_chan_spec()) with extended info. In that case, + * (through iio_backend_extend_chan_spec()) with extended info. In that case, * backends are not supposed to give their own callbacks (as they would not have * a way to get the backend from indio_dev). This is the setter. * @@ -851,7 +851,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_filter_type_set, "IIO_BACKEND"); * @back: Backend device * @timeout_us: Timeout value in us. * - * When activated, it initates a proccess that aligns the sample's most + * When activated, it initiates a process that aligns the sample's most * significant bit (MSB) based solely on the captured data, without * considering any other external signals. * @@ -1017,7 +1017,7 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con * @dev: Consumer device for the backend * @name: Backend name * - * Get's the backend associated with @dev. + * Gets the backend associated with @dev. * * RETURNS: * A backend pointer, negative error pointer otherwise. @@ -1050,7 +1050,7 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get_by_index, "IIO_BACKEND"); * @name: Backend name * @fwnode: Firmware node of the backend consumer * - * Get's the backend associated with a firmware node. + * Gets the backend associated with a firmware node. * * RETURNS: * A backend pointer, negative error pointer otherwise. diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 7e3d3872e2e6..767a7794624a 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -19,6 +19,7 @@ #include <linux/idr.h> #include <linux/kdev_t.h> #include <linux/kernel.h> +#include <linux/math64.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/poll.h> @@ -26,7 +27,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/wait.h> -#include <linux/wordpart.h> #include <linux/iio/buffer.h> #include <linux/iio/buffer_impl.h> @@ -99,6 +99,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_ATTENTION] = "attention", [IIO_ALTCURRENT] = "altcurrent", [IIO_COVERAGE] = "coverage", + [IIO_VOLUMEFLOW] = "volumeflow", }; static const char * const iio_modifier_names[] = { @@ -656,6 +657,7 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, int size, const int *vals) { int tmp0, tmp1; + int l = 0; s64 tmp2; bool scale_db = false; @@ -699,7 +701,6 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, case IIO_VAL_INT_MULTIPLE: { int i; - int l = 0; for (i = 0; i < size; ++i) l += sysfs_emit_at(buf, offset + l, "%d ", vals[i]); @@ -708,8 +709,26 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, case IIO_VAL_CHAR: return sysfs_emit_at(buf, offset, "%c", (char)vals[0]); case IIO_VAL_INT_64: - tmp2 = (s64)((((u64)vals[1]) << 32) | (u32)vals[0]); - return sysfs_emit_at(buf, offset, "%lld", tmp2); + return sysfs_emit_at(buf, offset, "%lld", + iio_val_s64_compose(vals[0], vals[1])); + case IIO_VAL_DECIMAL64_MILLI: + case IIO_VAL_DECIMAL64_MICRO: + case IIO_VAL_DECIMAL64_NANO: + case IIO_VAL_DECIMAL64_PICO: + case IIO_VAL_DECIMAL64_FEMTO: + { + int scale = type - IIO_VAL_DECIMAL64_BASE; + s64 frac; + + tmp2 = div64_s64_rem(iio_val_s64_compose(vals[0], vals[1]), + int_pow(10, scale), &frac); + if (tmp2 == 0 && frac < 0) + l += sysfs_emit_at(buf, offset, "-"); + + l += sysfs_emit_at(buf, offset + l, "%lld.%0*lld", tmp2, scale, + abs(frac)); + return l; + } default: return 0; } @@ -979,6 +998,7 @@ static ssize_t iio_write_channel_info(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); int ret, fract_mult = 100000; + int type, dec_scale = 0; int integer, fract = 0; long long integer64; bool is_char = false; @@ -989,9 +1009,11 @@ static ssize_t iio_write_channel_info(struct device *dev, if (!indio_dev->info->write_raw) return -EINVAL; - if (indio_dev->info->write_raw_get_fmt) - switch (indio_dev->info->write_raw_get_fmt(indio_dev, - this_attr->c, this_attr->address)) { + if (indio_dev->info->write_raw_get_fmt) { + type = indio_dev->info->write_raw_get_fmt(indio_dev, + this_attr->c, + this_attr->address); + switch (type) { case IIO_VAL_INT: fract_mult = 0; break; @@ -1007,12 +1029,20 @@ static ssize_t iio_write_channel_info(struct device *dev, case IIO_VAL_CHAR: is_char = true; break; + case IIO_VAL_DECIMAL64_MILLI: + case IIO_VAL_DECIMAL64_MICRO: + case IIO_VAL_DECIMAL64_NANO: + case IIO_VAL_DECIMAL64_PICO: + case IIO_VAL_DECIMAL64_FEMTO: + dec_scale = type - IIO_VAL_DECIMAL64_BASE; + fallthrough; case IIO_VAL_INT_64: is_64bit = true; break; default: return -EINVAL; } + } if (is_char) { char ch; @@ -1021,12 +1051,14 @@ static ssize_t iio_write_channel_info(struct device *dev, return -EINVAL; integer = ch; } else if (is_64bit) { - ret = kstrtoll(buf, 0, &integer64); + if (dec_scale) + ret = kstrtodec64(buf, dec_scale, &integer64); + else + ret = kstrtoll(buf, 0, &integer64); if (ret) return ret; - fract = upper_32_bits(integer64); - integer = lower_32_bits(integer64); + iio_val_s64_decompose(integer64, &integer, &fract); } else { ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract, scale_db); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9ce20cb05a9b..fb08e4f02520 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -281,7 +281,7 @@ struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, return ERR_PTR(-ENODEV); } -EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name); +EXPORT_SYMBOL_NS_GPL(fwnode_iio_channel_get_by_name, "IIO_CONSUMER"); static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) { @@ -386,7 +386,7 @@ struct iio_channel *iio_channel_get(struct device *dev, return iio_channel_get_sys(name, channel_name); } -EXPORT_SYMBOL_GPL(iio_channel_get); +EXPORT_SYMBOL_NS_GPL(iio_channel_get, "IIO_CONSUMER"); void iio_channel_release(struct iio_channel *channel) { @@ -395,7 +395,7 @@ void iio_channel_release(struct iio_channel *channel) iio_device_put(channel->indio_dev); kfree(channel); } -EXPORT_SYMBOL_GPL(iio_channel_release); +EXPORT_SYMBOL_NS_GPL(iio_channel_release, "IIO_CONSUMER"); static void devm_iio_channel_free(void *iio_channel) { @@ -418,7 +418,7 @@ struct iio_channel *devm_iio_channel_get(struct device *dev, return channel; } -EXPORT_SYMBOL_GPL(devm_iio_channel_get); +EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get, "IIO_CONSUMER"); struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, struct fwnode_handle *fwnode, @@ -437,7 +437,7 @@ struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, return channel; } -EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name); +EXPORT_SYMBOL_NS_GPL(devm_fwnode_iio_channel_get_by_name, "IIO_CONSUMER"); struct iio_channel *iio_channel_get_all(struct device *dev) { @@ -506,7 +506,7 @@ error_free_chans: iio_device_put(chans[i].indio_dev); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(iio_channel_get_all); +EXPORT_SYMBOL_NS_GPL(iio_channel_get_all, "IIO_CONSUMER"); void iio_channel_release_all(struct iio_channel *channels) { @@ -518,7 +518,7 @@ void iio_channel_release_all(struct iio_channel *channels) } kfree(channels); } -EXPORT_SYMBOL_GPL(iio_channel_release_all); +EXPORT_SYMBOL_NS_GPL(iio_channel_release_all, "IIO_CONSUMER"); static void devm_iio_channel_free_all(void *iio_channels) { @@ -541,7 +541,7 @@ struct iio_channel *devm_iio_channel_get_all(struct device *dev) return channels; } -EXPORT_SYMBOL_GPL(devm_iio_channel_get_all); +EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get_all, "IIO_CONSUMER"); static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum info) @@ -585,7 +585,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_raw, "IIO_CONSUMER"); int iio_read_channel_average_raw(struct iio_channel *chan, int *val) { @@ -597,7 +597,7 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val) return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); } -EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_average_raw, "IIO_CONSUMER"); int iio_multiply_value(int *result, s64 multiplier, unsigned int type, int val, int val2) @@ -701,7 +701,7 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, return iio_convert_raw_to_processed_unlocked(chan, raw, processed, scale); } -EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); +EXPORT_SYMBOL_NS_GPL(iio_convert_raw_to_processed, "IIO_CONSUMER"); int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum attribute) @@ -714,13 +714,13 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, return iio_channel_read(chan, val, val2, attribute); } -EXPORT_SYMBOL_GPL(iio_read_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_attribute, "IIO_CONSUMER"); int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2) { return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET); } -EXPORT_SYMBOL_GPL(iio_read_channel_offset); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_offset, "IIO_CONSUMER"); int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, unsigned int scale) @@ -752,20 +752,20 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, scale); } } -EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed_scale, "IIO_CONSUMER"); int iio_read_channel_processed(struct iio_channel *chan, int *val) { /* This is just a special case with scale factor 1 */ return iio_read_channel_processed_scale(chan, val, 1); } -EXPORT_SYMBOL_GPL(iio_read_channel_processed); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed, "IIO_CONSUMER"); int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) { return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE); } -EXPORT_SYMBOL_GPL(iio_read_channel_scale); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_scale, "IIO_CONSUMER"); static int iio_channel_read_avail(struct iio_channel *chan, const int **vals, int *type, int *length, @@ -794,7 +794,7 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan, return iio_channel_read_avail(chan, vals, type, length, attribute); } -EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_attribute, "IIO_CONSUMER"); int iio_read_avail_channel_raw(struct iio_channel *chan, const int **vals, int *length) @@ -811,7 +811,7 @@ int iio_read_avail_channel_raw(struct iio_channel *chan, return ret; } -EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_raw, "IIO_CONSUMER"); static int iio_channel_read_max(struct iio_channel *chan, int *val, int *val2, int *type, @@ -867,7 +867,7 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_max_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_max_channel_raw, "IIO_CONSUMER"); static int iio_channel_read_min(struct iio_channel *chan, int *val, int *val2, int *type, @@ -923,7 +923,7 @@ int iio_read_min_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_min_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_min_channel_raw, "IIO_CONSUMER"); int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { @@ -937,7 +937,7 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) return 0; } -EXPORT_SYMBOL_GPL(iio_get_channel_type); +EXPORT_SYMBOL_NS_GPL(iio_get_channel_type, "IIO_CONSUMER"); static int iio_channel_write(struct iio_channel *chan, int val, int val2, enum iio_chan_info_enum info) @@ -961,13 +961,13 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, return iio_channel_write(chan, val, val2, attribute); } -EXPORT_SYMBOL_GPL(iio_write_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_attribute, "IIO_CONSUMER"); int iio_write_channel_raw(struct iio_channel *chan, int val) { return iio_write_channel_attribute(chan, val, 0, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_write_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_raw, "IIO_CONSUMER"); unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) { @@ -982,7 +982,7 @@ unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) return i; } -EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count); +EXPORT_SYMBOL_NS_GPL(iio_get_channel_ext_info_count, "IIO_CONSUMER"); static const struct iio_chan_spec_ext_info * iio_lookup_ext_info(const struct iio_channel *chan, const char *attr) @@ -1017,7 +1017,7 @@ ssize_t iio_read_channel_ext_info(struct iio_channel *chan, return ext_info->read(chan->indio_dev, ext_info->private, chan->channel, buf); } -EXPORT_SYMBOL_GPL(iio_read_channel_ext_info); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_ext_info, "IIO_CONSUMER"); ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, const char *buf, size_t len) @@ -1031,7 +1031,7 @@ ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, return ext_info->write(chan->indio_dev, ext_info->private, chan->channel, buf, len); } -EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_ext_info, "IIO_CONSUMER"); ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) { @@ -1042,4 +1042,4 @@ ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf); } -EXPORT_SYMBOL_GPL(iio_read_channel_label); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_label, "IIO_CONSUMER"); diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c index 633a105fd7f0..0ea282ecc99a 100644 --- a/drivers/iio/light/adux1020.c +++ b/drivers/iio/light/adux1020.c @@ -808,10 +808,8 @@ static int adux1020_probe(struct i2c_client *client) NULL, adux1020_interrupt_handler, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, ADUX1020_DRV_NAME, indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) return ret; - } } return devm_iio_device_register(&client->dev, indio_dev); diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c index 8dacb1730429..d7e0924b8106 100644 --- a/drivers/iio/light/apds9160.c +++ b/drivers/iio/light/apds9160.c @@ -1545,11 +1545,8 @@ static int apds9160_probe(struct i2c_client *client) apds9160_irq_handler, IRQF_ONESHOT, "apds9160_event", indio_dev); - if (ret) { - return dev_err_probe(dev, ret, - "request irq (%d) failed\n", - client->irq); - } + if (ret) + return ret; } else { indio_dev->info = &apds9160_info_no_events; indio_dev->channels = apds9160_channels_without_events; diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index d60ade1209f3..f0abbc4a862a 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -432,10 +432,8 @@ static int apds9300_probe(struct i2c_client *client) NULL, apds9300_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "apds9300_event", indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) goto err; - } } ret = iio_device_register(indio_dev); diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index d582bda4d847..f681b02e4922 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -1284,8 +1284,7 @@ static int apds9306_probe(struct i2c_client *client) apds9306_irq_handler, IRQF_ONESHOT, "apds9306_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "failed to assign interrupt.\n"); + return ret; } else { indio_dev->info = &apds9306_info_no_events; indio_dev->channels = apds9306_channels_without_events; diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 2686c3b0c03b..afe520de60e2 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -1098,10 +1098,8 @@ static int apds9960_probe(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "apds9960_event", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + if (ret) goto error_power_down; - } ret = iio_device_register(indio_dev); if (ret) diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c index 0aa8e5cc6c56..2b8ff36071b8 100644 --- a/drivers/iio/light/bh1745.c +++ b/drivers/iio/light/bh1745.c @@ -784,8 +784,7 @@ static int bh1745_setup_triggered_buffer(struct iio_dev *indio_dev, IRQF_ONESHOT, "bh1745_interrupt", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "Request for IRQ failed\n"); + return ret; } return 0; diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index 2590fc8fd154..b32a94028f09 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -368,7 +368,7 @@ static int cm32181_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_CALIBSCALE: cm32181->calibscale = val; - return val; + return 0; case IIO_CHAN_INFO_INT_TIME: ret = cm32181_write_als_it(cm32181, val2); return ret; diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c index fbb9e0a13733..506c91f44f43 100644 --- a/drivers/iio/light/cm3323.c +++ b/drivers/iio/light/cm3323.c @@ -80,22 +80,19 @@ static int cm3323_init(struct iio_dev *indio_dev) { int ret; struct cm3323_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF); - if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_conf\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading reg_conf\n"); /* enable sensor and set auto force mode */ ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT); data->reg_conf = ret; ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, data->reg_conf); - if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_conf\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Error writing reg_conf\n"); return 0; } @@ -236,10 +233,8 @@ static int cm3323_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; ret = cm3323_init(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "cm3323 chip init failed\n"); + if (ret < 0) return ret; - } ret = devm_add_action_or_reset(&client->dev, cm3323_disable, indio_dev); if (ret < 0) diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 98c84f33db60..34c127593947 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -232,10 +232,8 @@ static int cm3605_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(dev, irq, cm3605_prox_irq, NULL, 0, "cm3605", indio_dev); - if (ret) { - dev_err(dev, "unable to request IRQ\n"); + if (ret) goto out_disable_aset; - } /* Just name the trigger the same as the driver */ led_trigger_register_simple("cm3605", &cm3605->led); @@ -324,3 +322,4 @@ module_platform_driver(cm3605_driver); MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index de29081fd3b2..1fd40d60182e 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -580,10 +580,8 @@ static int gp2ap002_probe(struct i2c_client *client) ret = devm_request_threaded_irq(dev, client->irq, NULL, gp2ap002_prox_irq, IRQF_ONESHOT, "gp2ap002", indio_dev); - if (ret) { - dev_err(dev, "unable to request IRQ\n"); + if (ret) goto out_put_pm; - } gp2ap002->irq = client->irq; /* @@ -731,3 +729,4 @@ module_i2c_driver(gp2ap002_driver); MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 6bf1bc9a38fe..929343e42d83 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -33,9 +33,9 @@ struct als_state { u32 illum[CHANNEL_SCAN_INDEX_MAX]; aligned_s64 timestamp; } scan; - int scale_pre_decml; - int scale_post_decml; - int scale_precision; + int scale_pre_decml[CHANNEL_SCAN_INDEX_MAX]; + int scale_post_decml[CHANNEL_SCAN_INDEX_MAX]; + int scale_precision[CHANNEL_SCAN_INDEX_MAX]; int value_offset; int num_channels; s64 timestamp; @@ -119,9 +119,8 @@ static const struct iio_chan_spec als_channels[] = { /* Channel read_raw handler */ static int als_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct als_state *als_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev = als_state->common_attributes.hsdev; @@ -162,12 +161,12 @@ static int als_read_raw(struct iio_dev *indio_dev, } if (report_id >= 0) { hid_sensor_power_state(&als_state->common_attributes, - true); + true); *val = sensor_hub_input_attr_get_raw_value( hsdev, hsdev->usage, address, report_id, SENSOR_HUB_SYNC, min < 0); hid_sensor_power_state(&als_state->common_attributes, - false); + false); } else { *val = 0; return -EINVAL; @@ -175,9 +174,11 @@ static int als_read_raw(struct iio_dev *indio_dev, ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = als_state->scale_pre_decml; - *val2 = als_state->scale_post_decml; - ret_type = als_state->scale_precision; + if (chan->scan_index >= CHANNEL_SCAN_INDEX_MAX) + return -EINVAL; + *val = als_state->scale_pre_decml[chan->scan_index]; + *val2 = als_state->scale_post_decml[chan->scan_index]; + ret_type = als_state->scale_precision[chan->scan_index]; break; case IIO_CHAN_INFO_OFFSET: *val = als_state->value_offset; @@ -205,10 +206,8 @@ static int als_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int als_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct als_state *als_state = iio_priv(indio_dev); int ret = 0; @@ -240,8 +239,7 @@ static const struct iio_info als_info = { /* Callback handler to send event after all samples are received and captured */ static int als_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); @@ -262,9 +260,9 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); @@ -303,9 +301,9 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int als_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - struct als_state *st) + struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct als_state *st) { struct iio_chan_spec *channels; int ret, index = 0; @@ -331,6 +329,10 @@ static int als_parse_report(struct platform_device *pdev, }; ++index; + st->scale_precision[i] = hid_sensor_format_scale(usage_id, + &st->als[i], &st->scale_pre_decml[i], + &st->scale_post_decml[i]); + dev_dbg(&pdev->dev, "als %x:%x\n", st->als[i].index, st->als[i].report_id); } @@ -340,10 +342,6 @@ static int als_parse_report(struct platform_device *pdev, if (index) ret = 0; - st->scale_precision = hid_sensor_format_scale(usage_id, - &st->als[CHANNEL_SCAN_INDEX_INTENSITY], - &st->scale_pre_decml, &st->scale_post_decml); - return ret; } @@ -399,31 +397,31 @@ static int hid_als_probe(struct platform_device *pdev) atomic_set(&als_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &als_state->common_attributes); + &als_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - als_state->callbacks.send_event = als_proc_event; als_state->callbacks.capture_sample = als_capture_sample; als_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, &als_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes); return ret; @@ -436,8 +434,8 @@ static void hid_als_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct als_state *als_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes); } diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 95bf2bfd86ee..63fd3eff171c 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -69,9 +69,8 @@ static const struct iio_chan_spec prox_channels[] = { /* Channel read_raw handler */ static int prox_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct prox_state *prox_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev; @@ -134,10 +133,8 @@ static int prox_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int prox_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct prox_state *prox_state = iio_priv(indio_dev); int ret = 0; @@ -165,8 +162,7 @@ static const struct iio_info prox_info = { /* Callback handler to send event after all samples are received and captured */ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); @@ -182,9 +178,9 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); @@ -217,8 +213,8 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int prox_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct prox_state *st) + struct hid_sensor_hub_device *hsdev, + struct prox_state *st) { struct iio_chan_spec *channels = st->channels; int index = 0; @@ -270,8 +266,7 @@ static int hid_prox_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct prox_state *prox_state; - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct prox_state)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct prox_state)); if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -305,18 +300,12 @@ static int hid_prox_probe(struct platform_device *pdev) atomic_set(&prox_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &prox_state->common_attributes); + &prox_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - prox_state->callbacks.send_event = prox_proc_event; prox_state->callbacks.capture_sample = prox_capture_sample; prox_state->callbacks.pdev = pdev; @@ -324,13 +313,19 @@ static int hid_prox_probe(struct platform_device *pdev) &prox_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes); return ret; @@ -343,8 +338,8 @@ static void hid_prox_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct prox_state *prox_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes); } diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index b88e7c4eae3e..33deb1726689 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -333,16 +333,6 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) return isl29028_read_als_ir(chip, ir_data); } -static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on) -{ - struct device *dev = regmap_get_device(chip->regmap); - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - /* Channel IO */ static int isl29028_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -352,7 +342,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, struct device *dev = regmap_get_device(chip->regmap); int ret; - ret = isl29028_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -405,11 +395,11 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; - ret = isl29028_set_pm_runtime_busy(chip, false); + ret = pm_runtime_put_autosuspend(dev); if (ret < 0) return ret; - return ret; + return 0; } static int isl29028_read_raw(struct iio_dev *indio_dev, @@ -420,7 +410,7 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, struct device *dev = regmap_get_device(chip->regmap); int ret, pm_ret; - ret = isl29028_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -476,10 +466,10 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, /** * Preserve the ret variable if the call to - * isl29028_set_pm_runtime_busy() is successful so the reading + * pm_runtime_put_autosuspend() is successful so the reading * (if applicable) is returned to user space. */ - pm_ret = isl29028_set_pm_runtime_busy(chip, false); + pm_ret = pm_runtime_put_autosuspend(dev); if (pm_ret < 0) return pm_ret; diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index bdc74b8226c8..bc031f2c3141 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -838,8 +838,7 @@ static int ltr390_probe(struct i2c_client *client) "ltr390_thresh_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "request irq (%d) failed\n", client->irq); + return ret; } ret = ltr390_pm_init(data); diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 7d045be78c6d..fa0d16890c8b 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1537,11 +1537,8 @@ static int ltr501_probe(struct i2c_client *client) IRQF_ONESHOT, "ltr501_thresh_event", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", - client->irq); + if (ret) return ret; - } } else { indio_dev->info = data->chip_info->info_no_irq; } diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index c5bd7fa23fb7..a948353014ac 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -10,6 +10,7 @@ #include <linux/array_size.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/dev_printk.h> #include <linux/errno.h> @@ -100,9 +101,14 @@ struct opt3001_chip_info { struct opt3001 { struct i2c_client *client; - struct device *dev; + /* + * Ensure data capture and read-modify-write sequences are + * not interrupted. + */ struct mutex lock; + + /* Allows for IRQs to bypass locking mechanism */ bool ok_to_ignore_lock; bool result_ready; wait_queue_head_t result_ready_queue; @@ -223,11 +229,10 @@ static const struct opt3001_scale opt3002_scales[] = { }, }; -static int opt3001_find_scale(const struct opt3001 *opt, int val, - int val2, u8 *exponent) +static int opt3001_find_scale(const struct opt3001 *opt, int val, int val2, + u8 *exponent) { - int i; - for (i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) { + for (unsigned int i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) { const struct opt3001_scale *scale = &(*opt->chip_info->scales)[i]; /* * Compare the integer and micro parts to determine value scale. @@ -242,8 +247,8 @@ static int opt3001_find_scale(const struct opt3001 *opt, int val, return -EINVAL; } -static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, - u16 mantissa, int *val, int *val2) +static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, u16 mantissa, + int *val, int *val2) { int ret; int whole = opt->chip_info->factor_whole; @@ -310,123 +315,143 @@ static const struct iio_chan_spec opt3002_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(1), }; -static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) +static int opt3001_start_conversion(struct opt3001 *opt) { - int ret; - u16 mantissa; + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; u16 reg; - u8 exponent; - u16 value; - long timeout; - - if (opt->use_irq) { - /* - * Enable the end-of-conversion interrupt mechanism. Note that - * doing so will overwrite the low-level limit value however we - * will restore this value later on. - */ - ret = i2c_smbus_write_word_swapped(opt->client, - OPT3001_LOW_LIMIT, - OPT3001_LOW_LIMIT_EOC_ENABLE); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); - return ret; - } - - /* Allow IRQ to access the device despite lock being set */ - opt->ok_to_ignore_lock = true; - } + int ret; /* Reset data-ready indicator flag */ opt->result_ready = false; /* Configure for single-conversion mode and start a new conversion */ - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - goto err; + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; } reg = ret; opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SINGLE); - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); + if (ret) + dev_err(dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); + + return ret; +} + +static int opt3001_get_processed_irq(struct opt3001 *opt) +{ + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + u16 value; + int ret; + + /* + * Enable the end-of-conversion interrupt mechanism. Note that doing so + * will overwrite the low-level limit value however we will restore this + * value later on. + */ + ret = i2c_smbus_write_word_swapped(client, + OPT3001_LOW_LIMIT, + OPT3001_LOW_LIMIT_EOC_ENABLE); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); - goto err; + dev_err(dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + return ret; } - if (opt->use_irq) { - /* Wait for the IRQ to indicate the conversion is complete */ - ret = wait_event_timeout(opt->result_ready_queue, - opt->result_ready, - msecs_to_jiffies(OPT3001_RESULT_READY_LONG)); - if (ret == 0) { - ret = -ETIMEDOUT; - goto err; - } - } else { - /* Sleep for result ready time */ - timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ? - OPT3001_RESULT_READY_SHORT : OPT3001_RESULT_READY_LONG; - msleep(timeout); - - /* Check result ready flag */ - ret = i2c_smbus_read_word_swapped(opt->client, - OPT3001_CONFIGURATION); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - goto err; - } + /* Allow IRQ to access the device despite lock being set */ + opt->ok_to_ignore_lock = true; - if (!(ret & OPT3001_CONFIGURATION_CRF)) { - ret = -ETIMEDOUT; - goto err; - } + ret = opt3001_start_conversion(opt); + if (ret) + goto err; - /* Obtain value */ - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_RESULT); - goto err; - } - opt->result = ret; - opt->result_ready = true; - } + if (wait_event_timeout(opt->result_ready_queue, opt->result_ready, + msecs_to_jiffies(OPT3001_RESULT_READY_LONG))) + ret = 0; + else + ret = -ETIMEDOUT; err: - if (opt->use_irq) - /* Disallow IRQ to access the device while lock is active */ - opt->ok_to_ignore_lock = false; + opt->ok_to_ignore_lock = false; if (ret < 0) return ret; - if (opt->use_irq) { - /* - * Disable the end-of-conversion interrupt mechanism by - * restoring the low-level limit value (clearing - * OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing - * those enable bits would affect the actual limit value due to - * bit-overlap and therefore can't be done. - */ - value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; - ret = i2c_smbus_write_word_swapped(opt->client, - OPT3001_LOW_LIMIT, - value); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); - return ret; - } + /* + * Disable the end-of-conversion interrupt mechanism by restoring the + * low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note + * that selectively clearing those enable bits would affect the actual + * limit value due to bit-overlap and therefore can't be done. + */ + value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; + ret = i2c_smbus_write_word_swapped(client, OPT3001_LOW_LIMIT, value); + if (ret) + dev_err(dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + + return ret; +} + +static int opt3001_get_processed_noirq(struct opt3001 *opt) +{ + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + int ret; + + ret = opt3001_start_conversion(opt); + if (ret) + return ret; + + if (opt->int_time == OPT3001_INT_TIME_SHORT) + msleep(OPT3001_RESULT_READY_SHORT); + else + msleep(OPT3001_RESULT_READY_LONG); + + /* Check result ready flag */ + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; } + if (!(ret & OPT3001_CONFIGURATION_CRF)) + return -ETIMEDOUT; + + /* Obtain value */ + ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); + if (ret < 0) { + dev_err(dev, "failed to read register %02x\n", + OPT3001_RESULT); + return ret; + } + + opt->result = ret; + opt->result_ready = true; + + return 0; +} + +static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) +{ + u16 mantissa; + u8 exponent; + int ret; + + if (opt->use_irq) + ret = opt3001_get_processed_irq(opt); + else + ret = opt3001_get_processed_noirq(opt); + if (ret) + return ret; + exponent = OPT3001_REG_EXPONENT(opt->result); mantissa = OPT3001_REG_MANTISSA(opt->result); @@ -445,13 +470,15 @@ static int opt3001_get_int_time(struct opt3001 *opt, int *val, int *val2) static int opt3001_set_int_time(struct opt3001 *opt, int time) { + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; u16 reg; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); return ret; } @@ -470,16 +497,14 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time) return -EINVAL; } - return i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + return i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); } static int opt3001_read_raw(struct iio_dev *iio, - struct iio_chan_spec const *chan, int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct opt3001 *opt = iio_priv(iio); - int ret; if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) return -EBUSY; @@ -487,31 +512,24 @@ static int opt3001_read_raw(struct iio_dev *iio, if (chan->type != opt->chip_info->chan_type) return -EINVAL; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: - ret = opt3001_get_processed(opt, val, val2); - break; + return opt3001_get_processed(opt, val, val2); case IIO_CHAN_INFO_INT_TIME: - ret = opt3001_get_int_time(opt, val, val2); - break; + return opt3001_get_int_time(opt, val, val2); default: - ret = -EINVAL; + return -EINVAL; } - - mutex_unlock(&opt->lock); - - return ret; } static int opt3001_write_raw(struct iio_dev *iio, - struct iio_chan_spec const *chan, int val, int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct opt3001 *opt = iio_priv(iio); - int ret; if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) return -EBUSY; @@ -525,47 +543,46 @@ static int opt3001_write_raw(struct iio_dev *iio, if (val != 0) return -EINVAL; - mutex_lock(&opt->lock); - ret = opt3001_set_int_time(opt, val2); - mutex_unlock(&opt->lock); + guard(mutex)(&opt->lock); - return ret; + return opt3001_set_int_time(opt, val2); } static int opt3001_read_event_value(struct iio_dev *iio, - 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) + 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 opt3001 *opt = iio_priv(iio); - int ret = IIO_VAL_INT_PLUS_MICRO; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); switch (dir) { case IIO_EV_DIR_RISING: opt3001_to_iio_ret(opt, opt->high_thresh_exp, - opt->high_thresh_mantissa, val, val2); - break; + opt->high_thresh_mantissa, val, val2); + return IIO_VAL_INT_PLUS_MICRO; case IIO_EV_DIR_FALLING: opt3001_to_iio_ret(opt, opt->low_thresh_exp, - opt->low_thresh_mantissa, val, val2); - break; + opt->low_thresh_mantissa, val, val2); + return IIO_VAL_INT_PLUS_MICRO; default: - ret = -EINVAL; + return -EINVAL; } - - mutex_unlock(&opt->lock); - - return ret; } static int opt3001_write_event_value(struct iio_dev *iio, - 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) + 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 opt3001 *opt = iio_priv(iio); + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; int whole; int integer; @@ -580,12 +597,12 @@ static int opt3001_write_event_value(struct iio_dev *iio, if (val < 0) return -EINVAL; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); ret = opt3001_find_scale(opt, val, val2, &exponent); if (ret < 0) { - dev_err(opt->dev, "can't find scale for %d.%06u\n", val, val2); - goto err; + dev_err(dev, "can't find scale for %d.%06u\n", val, val2); + return ret; } whole = opt->chip_info->factor_whole; @@ -608,25 +625,22 @@ static int opt3001_write_event_value(struct iio_dev *iio, opt->low_thresh_exp = exponent; break; default: - ret = -EINVAL; - goto err; + return -EINVAL; } - ret = i2c_smbus_write_word_swapped(opt->client, reg, value); + ret = i2c_smbus_write_word_swapped(client, reg, value); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", reg); - goto err; + dev_err(dev, "failed to write register %02x\n", reg); + return ret; } -err: - mutex_unlock(&opt->lock); - - return ret; + return 0; } static int opt3001_read_event_config(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) { struct opt3001 *opt = iio_priv(iio); @@ -634,10 +648,14 @@ static int opt3001_read_event_config(struct iio_dev *iio, } static int opt3001_write_event_config(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, bool state) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) { struct opt3001 *opt = iio_priv(iio); + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; u16 mode; u16 reg; @@ -648,33 +666,29 @@ static int opt3001_write_event_config(struct iio_dev *iio, if (!state && opt->mode == OPT3001_CONFIGURATION_M_SHUTDOWN) return 0; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS : OPT3001_CONFIGURATION_M_SHUTDOWN; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - goto err; + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; } reg = ret; opt3001_set_mode(opt, ®, mode); - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); - goto err; + dev_err(dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); + return ret; } -err: - mutex_unlock(&opt->lock); - - return ret; + return 0; } static const struct iio_info opt3001_info = { @@ -689,47 +703,69 @@ static const struct iio_info opt3001_info = { static int opt3001_read_id(struct opt3001 *opt) { + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; char manufacturer[2]; u16 device_id; int ret; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_MANUFACTURER_ID); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_MANUFACTURER_ID); - return ret; - } + ret = i2c_smbus_read_word_swapped(client, OPT3001_MANUFACTURER_ID); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_MANUFACTURER_ID); manufacturer[0] = ret >> 8; manufacturer[1] = ret & 0xff; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_DEVICE_ID); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_DEVICE_ID); - return ret; - } + ret = i2c_smbus_read_word_swapped(client, OPT3001_DEVICE_ID); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_DEVICE_ID); device_id = ret; - dev_info(opt->dev, "Found %c%c OPT%04x\n", manufacturer[0], - manufacturer[1], device_id); + dev_info(dev, "Found %c%c OPT%04x\n", manufacturer[0], manufacturer[1], + device_id); return 0; } -static int opt3001_configure(struct opt3001 *opt) +static void opt3001_power_off(void *data) { + struct opt3001 *opt = data; + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + u16 reg_val; int ret; - u16 reg; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - return ret; + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return; } + reg_val = ret; + opt3001_set_mode(opt, ®_val, OPT3001_CONFIGURATION_M_SHUTDOWN); + + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg_val); + if (ret < 0) + dev_err(dev, "failed to write to register %02x\n", + OPT3001_CONFIGURATION); +} + +static int opt3001_configure(struct opt3001 *opt) +{ + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + int ret; + u16 reg; + + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + reg = ret; /* Enable automatic full-scale setting mode */ @@ -751,30 +787,28 @@ static int opt3001_configure(struct opt3001 *opt) reg &= ~OPT3001_CONFIGURATION_ME; reg &= ~OPT3001_CONFIGURATION_FC_MASK; - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); - return ret; - } + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to write register %02x\n", + OPT3001_CONFIGURATION); - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_LOW_LIMIT); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_LOW_LIMIT); - return ret; - } + ret = devm_add_action_or_reset(dev, opt3001_power_off, opt); + if (ret) + return dev_err_probe(dev, ret, + "failed to register power off function\n"); + + ret = i2c_smbus_read_word_swapped(client, OPT3001_LOW_LIMIT); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_LOW_LIMIT); opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret); opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret); - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_HIGH_LIMIT); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_HIGH_LIMIT); - return ret; - } + ret = i2c_smbus_read_word_swapped(client, OPT3001_HIGH_LIMIT); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_HIGH_LIMIT); opt->high_thresh_mantissa = OPT3001_REG_MANTISSA(ret); opt->high_thresh_exp = OPT3001_REG_EXPONENT(ret); @@ -786,6 +820,8 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) { struct iio_dev *iio = _iio; struct opt3001 *opt = iio_priv(iio); + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; bool wake_result_ready_queue = false; enum iio_chan_type chan_type = opt->chip_info->chan_type; @@ -794,10 +830,10 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) if (!ok_to_ignore_lock) mutex_lock(&opt->lock); - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); goto out; } @@ -805,21 +841,21 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) OPT3001_CONFIGURATION_M_CONTINUOUS) { if (ret & OPT3001_CONFIGURATION_FH) iio_push_event(iio, - IIO_UNMOD_EVENT_CODE(chan_type, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns(iio)); + IIO_UNMOD_EVENT_CODE(chan_type, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(iio)); if (ret & OPT3001_CONFIGURATION_FL) iio_push_event(iio, - IIO_UNMOD_EVENT_CODE(chan_type, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - iio_get_time_ns(iio)); + IIO_UNMOD_EVENT_CODE(chan_type, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(iio)); } else if (ret & OPT3001_CONFIGURATION_CRF) { - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); + ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_RESULT); + dev_err(dev, "failed to read register %02x\n", + OPT3001_RESULT); goto out; } opt->result = ret; @@ -852,12 +888,13 @@ static int opt3001_probe(struct i2c_client *client) opt = iio_priv(iio); opt->client = client; - opt->dev = dev; opt->chip_info = i2c_get_match_data(client); - mutex_init(&opt->lock); + ret = devm_mutex_init(dev, &opt->lock); + if (ret) + return ret; + init_waitqueue_head(&opt->result_ready_queue); - i2c_set_clientdata(client, iio); if (opt->chip_info->has_id) { ret = opt3001_read_id(opt); @@ -875,55 +912,20 @@ static int opt3001_probe(struct i2c_client *client) iio->modes = INDIO_DIRECT_MODE; iio->info = &opt3001_info; - ret = devm_iio_device_register(dev, iio); - if (ret) { - dev_err(dev, "failed to register IIO device\n"); - return ret; - } - /* Make use of INT pin only if valid IRQ no. is given */ if (irq > 0) { - ret = request_threaded_irq(irq, NULL, opt3001_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "opt3001", iio); - if (ret) { - dev_err(dev, "failed to request IRQ #%d\n", irq); + ret = devm_request_threaded_irq(dev, irq, NULL, opt3001_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "opt3001", iio); + if (ret) return ret; - } + opt->use_irq = true; } else { - dev_dbg(opt->dev, "enabling interrupt-less operation\n"); - } - - return 0; -} - -static void opt3001_remove(struct i2c_client *client) -{ - struct iio_dev *iio = i2c_get_clientdata(client); - struct opt3001 *opt = iio_priv(iio); - int ret; - u16 reg; - - if (opt->use_irq) - free_irq(client->irq, iio); - - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - return; + dev_dbg(dev, "enabling interrupt-less operation\n"); } - reg = ret; - opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN); - - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); - } + return devm_iio_device_register(dev, iio); } static const struct opt3001_chip_info opt3001_chip_information = { @@ -964,7 +966,6 @@ MODULE_DEVICE_TABLE(of, opt3001_of_match); static struct i2c_driver opt3001_driver = { .probe = opt3001_probe, - .remove = opt3001_remove, .id_table = opt3001_id, .driver = { diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index f79dd342937d..8253a6a8bee4 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -1205,7 +1205,7 @@ static int opt4060_setup_trigger(struct opt4060_chip *chip, struct iio_dev *idev IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name, idev); if (ret) - return dev_err_probe(chip->dev, ret, "Could not request IRQ\n"); + return ret; init_completion(&chip->completion); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index f961973892f2..7d82efc31f3a 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -987,11 +987,8 @@ static int rpr0521_probe(struct i2c_client *client) rpr0521_drdy_irq_handler, rpr0521_drdy_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rpr0521_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d for trigger0 failed\n", - client->irq); + if (ret) goto err_pm_disable; - } ret = devm_iio_trigger_register(indio_dev->dev.parent, data->drdy_trigger0); @@ -1071,7 +1068,10 @@ static int rpr0521_runtime_resume(struct device *dev) struct rpr0521_data *data = iio_priv(indio_dev); int ret; - regcache_sync(data->regmap); + ret = regcache_sync(data->regmap); + if (ret < 0) + return ret; + if (data->als_ps_need_en) { ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE); if (ret < 0) diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index 4601ae5d2009..bb7c99041819 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -1251,10 +1251,8 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev) IRQF_TRIGGER_FALLING | IRQF_NO_THREAD, "si1145_irq", trig); - if (ret < 0) { - dev_err(&client->dev, "irq request failed\n"); + if (ret) return ret; - } ret = devm_iio_trigger_register(&client->dev, trig); if (ret) diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index bcd729a9924e..94d5261b2663 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -196,11 +196,8 @@ static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev) st_uvis25_trigger_handler_thread, irq_type | IRQF_ONESHOT, iio_dev->name, hw); - if (err) { - dev_err(dev, "failed to request trigger irq %d\n", - hw->irq); + if (err) return err; - } hw->trig = devm_iio_trigger_alloc(dev, "%s-trigger", iio_dev->name); diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c index c4c15093e9e5..0f78a0abfafb 100644 --- a/drivers/iio/light/st_uvis25_spi.c +++ b/drivers/iio/light/st_uvis25_spi.c @@ -46,7 +46,7 @@ static const struct of_device_id st_uvis25_spi_of_match[] = { MODULE_DEVICE_TABLE(of, st_uvis25_spi_of_match); static const struct spi_device_id st_uvis25_spi_id_table[] = { - { ST_UVIS25_DEV_NAME }, + { .name = ST_UVIS25_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table); diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index e7ce6f32592b..7c8a1d2b2ed0 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -673,11 +673,8 @@ static int stk3310_probe(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "stk3310_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto err_standby; - } } ret = iio_device_register(indio_dev); diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index b61442c5c1be..b392943e51c8 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -9,8 +9,6 @@ * TCS34727) * * Datasheet: http://ams.com/eng/content/download/319364/1117183/file/TCS3472_Datasheet_EN_v2.pdf - * - * TODO: wait time */ #include <linux/cleanup.h> @@ -19,6 +17,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/pm.h> +#include <linux/units.h> #include <linux/iio/buffer.h> #include <linux/iio/events.h> @@ -53,19 +52,30 @@ #define TCS3472_STATUS_AINT BIT(4) #define TCS3472_STATUS_AVALID BIT(0) #define TCS3472_ENABLE_AIEN BIT(4) +#define TCS3472_ENABLE_WEN BIT(3) #define TCS3472_ENABLE_AEN BIT(1) #define TCS3472_ENABLE_PON BIT(0) +#define TCS3472_ENABLE_RUN \ + (TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON | TCS3472_ENABLE_WEN) #define TCS3472_CONTROL_AGAIN_MASK (BIT(0) | BIT(1)) +#define TCS3472_CONFIG_WLONG BIT(1) + +#define TCS3472_ATIME_TO_US(atime) (((256) - (atime)) * 2400) struct tcs3472_data { struct i2c_client *client; struct mutex lock; + int target_freq_hz; + int target_freq_uhz; u16 low_thresh; u16 high_thresh; u8 enable; + u8 enable_pre_suspend; u8 control; u8 atime; u8 apers; + u8 wtime; + bool wlong; }; static const struct iio_event_spec tcs3472_events[] = { @@ -91,6 +101,7 @@ static const struct iio_event_spec tcs3472_events[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .channel2 = IIO_MOD_LIGHT_##_color, \ .address = _addr, \ .scan_index = _si, \ @@ -114,9 +125,65 @@ static const struct iio_chan_spec tcs3472_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; +/* + * The chip's cycle time is the sum of three components: + * - ATIME: the programmable RGBC integration time. + * - The fixed RGBC initialization time (2400 us). + * - WTIME: the wait time, used only if WEN is set. If WLONG is active, + * the wait step is multiplied by 12 (2400 us -> 28800 us). + */ +static unsigned int tcs3472_cycle_time_us(struct tcs3472_data *data) +{ + unsigned int atime_us = TCS3472_ATIME_TO_US(data->atime); + unsigned int init_us = 2400; + unsigned int wtime_us; + + if (!(data->enable & TCS3472_ENABLE_WEN)) + wtime_us = 0; + else if (data->wlong) + wtime_us = (256 - data->wtime) * 28800; + else + wtime_us = (256 - data->wtime) * 2400; + + return atime_us + init_us + wtime_us; +} + +/* + * Convert a cycle time in microseconds to a frequency in Hz and microhertz. + * + * Given cycle_us = T (the cycle period in microseconds), the corresponding + * frequency is: + * f = 1e6 / T [Hz] + * + * The result is split into the IIO_VAL_INT_PLUS_MICRO format: + * val = floor(1e6 / T) [Hz] + * val2 = (1e6 mod T) * 1e6 / T [microhertz] + */ +static void tcs3472_cycle_to_freq(unsigned int cycle_us, int *val, int *val2) +{ + *val = USEC_PER_SEC / cycle_us; + *val2 = div_u64((u64)(USEC_PER_SEC % cycle_us) * USEC_PER_SEC, + cycle_us); +} + static int tcs3472_req_data(struct tcs3472_data *data) { - int tries = 50; + /* + * The worst-case cycle time is reached with ATIME=0x00, WTIME=0x00 + * and WLONG=1. So: + * 614 ms (Max Integration Time) + * + 2.4 ms (RGBC Init) + * + 7.37 s (Max Wait Time) + * = ~ 8 s (Total Max cycle time). + * Use that as a polling upper bound; in normal operation the loop + * exits as soon as AVALID is set. So the total number of tries in 8 + * seconds considering a polling period of 20 ms is 400. + * Considering a 20% margin due to oscillator tolerance, the total + * duration becomes approximately 9.8 seconds, which corresponds to + * about 480 steps. Therefore, setting it to 500 appears to be a + * reasonable and safe trade-off. + */ + int tries = 500; int ret; while (tries--) { @@ -164,18 +231,165 @@ static int tcs3472_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_INT_TIME: *val = 0; - *val2 = (256 - data->atime) * 2400; + *val2 = TCS3472_ATIME_TO_US(data->atime); return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: { + unsigned int cycle_us; + + guard(mutex)(&data->lock); + cycle_us = tcs3472_cycle_time_us(data); + tcs3472_cycle_to_freq(cycle_us, val, val2); + return IIO_VAL_INT_PLUS_MICRO; + } default: return -EINVAL; } } +/* + * __tcs3472_set_sampling_freq() - implementation of sampling frequency + * configuration. The caller must hold data->lock. + */ +static int __tcs3472_set_sampling_freq(struct tcs3472_data *data, + int val, int val2) +{ + unsigned int atime_us; + unsigned int init_us = 2400; + u64 cycle_us; + s64 wait_us; + int wtime; + bool wlong = false; + u8 config; + int ret; + + if (val < 0 || val2 < 0 || (val == 0 && val2 == 0)) + return -EINVAL; + + atime_us = TCS3472_ATIME_TO_US(data->atime); + + /* + * cycle_us = 1 / freq, expressed in microseconds. + * Numerator: 1 [s] = PSEC_PER_SEC [ps] + * Denominator: freq [Hz] * MICROHZ_PER_HZ + val2 [uHz] = freq in [uHz] + * Result: ps / uHz = us + */ + cycle_us = div64_u64(PSEC_PER_SEC, (u64)val * MICROHZ_PER_HZ + val2); + + /* + * wait_us can be negative when the requested frequency is too high + * to be reached, or very large when the requested frequency is + * close to zero. Use s64 to cover the full range: + * + * cycle_us = PSEC_PER_SEC / (val * MICROHZ_PER_HZ + val2) + * + * The divisor of the formula above reaches its maximum when + * val = val2 = INT_MAX: + * INT_MAX * MICROHZ_PER_HZ + INT_MAX = ~2.15e18 + * so cycle_us_min = floor(1e12 / 2.15e18) = 0. + * + * The divisor reaches its minimum (1) when val = 0 and val2 = 1, + * so cycle_us_max = 1e12 / 1 = 1e12. + * + * Therefore: + * wait_us_min = 0 - 2400 - 612000 = -616800 + * wait_us_max = 1e12 - 2400 - 2400 = 999999995200 + * + * Both fit comfortably in s64. + */ + wait_us = (s64)cycle_us - init_us - atime_us; + if (wait_us < 2400) { + if (data->enable & TCS3472_ENABLE_WEN) { + u8 enable = data->enable & ~TCS3472_ENABLE_WEN; + + ret = i2c_smbus_write_byte_data(data->client, + TCS3472_ENABLE, enable); + if (ret) + return ret; + + data->enable = enable; + } + + data->target_freq_hz = val; + data->target_freq_uhz = val2; + return 0; + } + + /* + * Wait state is needed: make sure WEN is active before programming + * WTIME (and possibly WLONG). + */ + if (!(data->enable & TCS3472_ENABLE_WEN)) { + u8 enable = data->enable | TCS3472_ENABLE_WEN; + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, + enable); + if (ret) + return ret; + + data->enable = enable; + } + + wtime = 256 - DIV_ROUND_CLOSEST_ULL(wait_us, 2400); + if (wtime < 0) { + /* + * If wait_us is too high (so the requested frequency is too + * low), the resulting wait exceeds what WTIME can represent + * (max 614 ms without WLONG). Enable WLONG, whose step is 12x + * longer (28.8 ms instead of 2.4 ms), and recompute. + */ + wlong = true; + wtime = 256 - DIV_ROUND_CLOSEST_ULL(wait_us, 28800); + } + + if (wlong != data->wlong) { + ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONFIG); + if (ret < 0) + return ret; + + config = ret; + if (wlong) + config |= TCS3472_CONFIG_WLONG; + else + config &= ~TCS3472_CONFIG_WLONG; + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_CONFIG, + config); + if (ret) + return ret; + + data->wlong = wlong; + } + + /* + * If the requested wait is so long that even WLONG cannot + * cover it, wtime may still be negative. Saturate to 0, + * which is the largest possible wait (256 * 28.8 ms = 7.37 s). + */ + wtime = clamp(wtime, 0, 255); + ret = i2c_smbus_write_byte_data(data->client, TCS3472_WTIME, wtime); + if (ret) + return ret; + + data->wtime = wtime; + data->target_freq_hz = val; + data->target_freq_uhz = val2; + + return 0; +} + +static int tcs3472_set_sampling_freq(struct tcs3472_data *data, + int val, int val2) +{ + guard(mutex)(&data->lock); + return __tcs3472_set_sampling_freq(data, val, val2); +} + static int tcs3472_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct tcs3472_data *data = iio_priv(indio_dev); + int ret; int i; switch (mask) { @@ -196,15 +410,31 @@ static int tcs3472_write_raw(struct iio_dev *indio_dev, if (val != 0) return -EINVAL; for (i = 0; i < 256; i++) { - if (val2 == (256 - i) * 2400) { - data->atime = i; - return i2c_smbus_write_byte_data( - data->client, TCS3472_ATIME, - data->atime); - } - + if (val2 != (256 - i) * 2400) + continue; + + guard(mutex)(&data->lock); + + ret = i2c_smbus_write_byte_data(data->client, + TCS3472_ATIME, i); + if (ret) + return ret; + + data->atime = i; + + /* + * ATIME just changed, so the cycle time changed too. + * Re-run the sampling frequency logic to recompute + * WTIME and preserve the user's last requested + * frequency. Lock is already held. + */ + return __tcs3472_set_sampling_freq(data, + data->target_freq_hz, + data->target_freq_uhz); } return -EINVAL; + case IIO_CHAN_INFO_SAMP_FREQ: + return tcs3472_set_sampling_freq(data, val, val2); default: return -EINVAL; } @@ -234,7 +464,7 @@ static int tcs3472_read_event(struct iio_dev *indio_dev, data->high_thresh : data->low_thresh; return IIO_VAL_INT; case IIO_EV_INFO_PERIOD: - period = (256 - data->atime) * 2400 * + period = tcs3472_cycle_time_us(data) * tcs3472_intr_pers[data->apers]; *val = period / USEC_PER_SEC; *val2 = period % USEC_PER_SEC; @@ -279,11 +509,13 @@ static int tcs3472_write_event(struct iio_dev *indio_dev, data->low_thresh = val; return 0; - case IIO_EV_INFO_PERIOD: + case IIO_EV_INFO_PERIOD:{ + unsigned int cycle_us; + period = val * USEC_PER_SEC + val2; + cycle_us = tcs3472_cycle_time_us(data); for (i = 1; i < ARRAY_SIZE(tcs3472_intr_pers) - 1; i++) { - if (period <= (256 - data->atime) * 2400 * - tcs3472_intr_pers[i]) + if (period <= cycle_us * tcs3472_intr_pers[i]) break; } ret = i2c_smbus_write_byte_data(data->client, TCS3472_PERS, i); @@ -293,6 +525,7 @@ static int tcs3472_write_event(struct iio_dev *indio_dev, data->apers = i; return 0; + } default: return -EINVAL; } @@ -434,17 +667,16 @@ static const struct iio_info tcs3472_info = { static int tcs3472_powerdown(struct tcs3472_data *data) { int ret; - u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; guard(mutex)(&data->lock); + data->enable_pre_suspend = data->enable; + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable & ~enable_mask); + data->enable & ~TCS3472_ENABLE_RUN); if (ret) return ret; - data->enable &= ~enable_mask; - return 0; } @@ -458,7 +690,9 @@ static int tcs3472_probe(struct i2c_client *client) struct device *dev = &client->dev; struct tcs3472_data *data; struct iio_dev *indio_dev; + unsigned int cycle_us; int ret; + u8 enable; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -498,6 +732,16 @@ static int tcs3472_probe(struct i2c_client *client) return ret; data->atime = ret; + ret = i2c_smbus_read_byte_data(data->client, TCS3472_WTIME); + if (ret < 0) + return ret; + data->wtime = ret; + + ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONFIG); + if (ret < 0) + return ret; + data->wlong = (ret & TCS3472_CONFIG_WLONG) ? 1 : 0; + ret = i2c_smbus_read_word_data(data->client, TCS3472_AILT); if (ret < 0) return ret; @@ -518,14 +762,30 @@ static int tcs3472_probe(struct i2c_client *client) if (ret < 0) return ret; - /* enable device */ - data->enable = ret | TCS3472_ENABLE_PON | TCS3472_ENABLE_AEN; - data->enable &= ~TCS3472_ENABLE_AIEN; - ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable); + /* + * Enable the chip in its full running state, including WEN. The + * actual wait time is controlled by the WTIME and WLONG registers, + * which retain their power-on defaults until userspace writes to + * sampling_frequency. + */ + enable = (ret | TCS3472_ENABLE_RUN) & ~TCS3472_ENABLE_AIEN; + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, enable); if (ret < 0) return ret; + data->enable = enable; + + /* + * Initialize target frequency from the chip's current state so that + * subsequent integration_time changes via IIO_CHAN_INFO_INT_TIME can + * preserve a meaningful sampling rate, even before userspace writes + * sampling_frequency for the first time. + */ + cycle_us = tcs3472_cycle_time_us(data); + tcs3472_cycle_to_freq(cycle_us, &data->target_freq_hz, + &data->target_freq_uhz); + ret = devm_add_action_or_reset(dev, tcs3472_powerdown_action, data); if (ret) return ret; @@ -561,16 +821,21 @@ static int tcs3472_resume(struct device *dev) struct tcs3472_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); int ret; - u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; guard(mutex)(&data->lock); + /* + * Restore the full ENABLE register from the snapshot taken in + * tcs3472_powerdown(). This preserves the user's last + * sampling_frequency configuration (in particular the WEN bit) + * across suspend/resume. + */ ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable | enable_mask); + data->enable_pre_suspend); if (ret) return ret; - data->enable |= enable_mask; + data->enable = data->enable_pre_suspend; return 0; } diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index 45f3513d931e..7bef0c3118c2 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -754,7 +754,7 @@ static int tsl2563_probe(struct i2c_client *client) "tsl2563_event", indio_dev); if (err) - return dev_err_probe(dev, err, "irq request error\n"); + return err; } err = tsl2563_configure(chip); diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index a0dd122af2cf..53fd423aa7ae 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -456,12 +456,8 @@ static int tsl2583_chip_init_and_power_on(struct iio_dev *indio_dev) usleep_range(3000, 3500); - ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON | - TSL2583_CNTL_ADC_ENBL); - if (ret < 0) - return ret; - - return ret; + return tsl2583_set_power_state(chip, + TSL2583_CNTL_PWR_ON | TSL2583_CNTL_ADC_ENBL); } /* Sysfs Interface Functions */ @@ -475,7 +471,7 @@ static ssize_t in_illuminance_input_target_show(struct device *dev, int ret; mutex_lock(&chip->als_mutex); - ret = sprintf(buf, "%d\n", chip->als_settings.als_cal_target); + ret = sysfs_emit(buf, "%d\n", chip->als_settings.als_cal_target); mutex_unlock(&chip->als_mutex); return ret; @@ -533,10 +529,10 @@ static ssize_t in_illuminance_lux_table_show(struct device *dev, int offset = 0; for (i = 0; i < ARRAY_SIZE(chip->als_settings.als_device_lux); i++) { - offset += sprintf(buf + offset, "%u,%u,%u,", - chip->als_settings.als_device_lux[i].ratio, - chip->als_settings.als_device_lux[i].ch0, - chip->als_settings.als_device_lux[i].ch1); + offset += sysfs_emit_at(buf, offset, "%u,%u,%u,", + chip->als_settings.als_device_lux[i].ratio, + chip->als_settings.als_device_lux[i].ch0, + chip->als_settings.als_device_lux[i].ch1); if (chip->als_settings.als_device_lux[i].ratio == 0) { /* * We just printed the first "0" entry. @@ -547,7 +543,7 @@ static ssize_t in_illuminance_lux_table_show(struct device *dev, } } - offset += sprintf(buf + offset, "\n"); + offset += sysfs_emit_at(buf, offset, "\n"); return offset; } @@ -639,14 +635,6 @@ static const struct iio_chan_spec tsl2583_channels[] = { }, }; -static int tsl2583_set_pm_runtime_busy(struct tsl2583_chip *chip, bool on) -{ - if (on) - return pm_runtime_resume_and_get(&chip->client->dev); - - return pm_runtime_put_autosuspend(&chip->client->dev); -} - static int tsl2583_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -654,7 +642,7 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, struct tsl2583_chip *chip = iio_priv(indio_dev); int ret, pm_ret; - ret = tsl2583_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(&chip->client->dev); if (ret < 0) return ret; @@ -722,16 +710,16 @@ read_done: mutex_unlock(&chip->als_mutex); if (ret < 0) { - tsl2583_set_pm_runtime_busy(chip, false); + pm_runtime_put_autosuspend(&chip->client->dev); return ret; } /* * Preserve the ret variable if the call to - * tsl2583_set_pm_runtime_busy() is successful so the reading + * pm_runtime_put_autosuspend() is successful so the reading * (if applicable) is returned to user space. */ - pm_ret = tsl2583_set_pm_runtime_busy(chip, false); + pm_ret = pm_runtime_put_autosuspend(&chip->client->dev); if (pm_ret < 0) return pm_ret; @@ -745,7 +733,7 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, struct tsl2583_chip *chip = iio_priv(indio_dev); int ret; - ret = tsl2583_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(&chip->client->dev); if (ret < 0) return ret; @@ -786,15 +774,15 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->als_mutex); if (ret < 0) { - tsl2583_set_pm_runtime_busy(chip, false); + pm_runtime_put_autosuspend(&chip->client->dev); return ret; } - ret = tsl2583_set_pm_runtime_busy(chip, false); + ret = pm_runtime_put_autosuspend(&chip->client->dev); if (ret < 0) return ret; - return ret; + return 0; } static const struct iio_info tsl2583_info = { diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 244f44379c36..4486a1d9d84d 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -1274,7 +1274,7 @@ static int tsl2772_read_raw(struct iio_dev *indio_dev, } break; case IIO_CHAN_INFO_CALIBSCALE: - if (chan->type == IIO_LIGHT) + if (chan->type == IIO_INTENSITY) *val = tsl2772_als_gain[chip->settings.als_gain]; else *val = tsl2772_prox_gain[chip->settings.prox_gain]; @@ -1849,11 +1849,8 @@ static int tsl2772_probe(struct i2c_client *clientp) IRQF_ONESHOT, "TSL2772_event", indio_dev); - if (ret) { - dev_err(&clientp->dev, - "%s: irq request failed\n", __func__); + if (ret) return ret; - } } else { indio_dev->channels = chip->chip_info->channel_without_events; } diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 128ae3f94074..336468d59ea7 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -537,16 +537,6 @@ static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) return !!(ret & VCNL4000_SELF_TIMED_EN); } -static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) -{ - struct device *dev = &data->client->dev; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2) { int ret; @@ -850,7 +840,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = vcnl4000_set_pm_runtime_state(data, true); + ret = pm_runtime_resume_and_get(&data->client->dev); if (ret < 0) return ret; @@ -869,7 +859,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, default: ret = -EINVAL; } - vcnl4000_set_pm_runtime_state(data, false); + pm_runtime_put_autosuspend(&data->client->dev); return ret; case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_LIGHT) @@ -1922,7 +1912,7 @@ static void vcnl4000_cleanup(void *data) static int vcnl4000_probe(struct i2c_client *client) { - const char * const regulator_names[] = { "vdd", "vio", "vled" }; + static const char * const regulator_names[] = { "vdd", "vio", "vled" }; struct device *dev = &client->dev; struct vcnl4000_data *data; struct iio_dev *indio_dev; diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index bf3a49b4351d..ce1bd42b0c7c 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -145,16 +145,6 @@ static const struct iio_trigger_ops vcnl4035_trigger_ops = { .set_trigger_state = vcnl4035_als_drdy_set_state, }; -static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on) -{ - struct device *dev = &data->client->dev; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - static int vcnl4035_read_info_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { @@ -202,11 +192,11 @@ static int vcnl4035_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = vcnl4035_set_pm_runtime_state(data, true); + ret = pm_runtime_resume_and_get(&data->client->dev); if (ret < 0) return ret; ret = vcnl4035_read_info_raw(indio_dev, chan, val); - vcnl4035_set_pm_runtime_state(data, false); + pm_runtime_put_autosuspend(&data->client->dev); return ret; case IIO_CHAN_INFO_INT_TIME: *val = 50; @@ -237,7 +227,7 @@ static int vcnl4035_write_raw(struct iio_dev *indio_dev, if (val <= 0 || val > 800) return -EINVAL; - ret = vcnl4035_set_pm_runtime_state(data, true); + ret = pm_runtime_resume_and_get(&data->client->dev); if (ret < 0) return ret; @@ -247,7 +237,7 @@ static int vcnl4035_write_raw(struct iio_dev *indio_dev, if (!ret) data->als_it_val = val / 100; - vcnl4035_set_pm_runtime_state(data, false); + pm_runtime_put_autosuspend(&data->client->dev); return ret; default: return -EINVAL; @@ -647,7 +637,10 @@ static int vcnl4035_runtime_resume(struct device *dev) struct vcnl4035_data *data = iio_priv(indio_dev); int ret; - regcache_sync(data->regmap); + ret = regcache_sync(data->regmap); + if (ret < 0) + return ret; + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE); if (ret < 0) return ret; diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 7c837d60dad8..ec9c127e7d8a 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -921,9 +921,7 @@ static int veml6030_set_info(struct iio_dev *indio_dev) IRQF_TRIGGER_LOW | IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret < 0) - return dev_err_probe(&client->dev, ret, - "irq %d request failed\n", - client->irq); + return ret; indio_dev->info = &veml6030_info; } else { diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 4f270f405b21..aef8a5333442 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -721,7 +721,7 @@ static int vl6180_probe(struct i2c_client *client) IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(&client->dev, ret, "devm_request_irq error\n"); + return ret; init_completion(&data->completion); diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index ea2697fb5ab6..d359003b248e 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -209,6 +209,28 @@ config INFINEON_TLV493D To compile this driver as a module, choose M here: the module will be called tlv493d. +config QMC5883L + tristate "QST QMC5883L 3-Axis Magnetic Sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to add support driver for QST QMC5883L 3-Axis + Magnetic Sensor. + + To compile this driver as a module, choose M here: the + module will be called qmc5883l. + +config QMC6308 + tristate "QST QMC6308 3-Axis Magnetic Sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to add support for the QST QMC6308 3-Axis + Magnetic Sensor. + + To compile this driver as a module, choose M here: the + module will be called qmc6308. + config SENSORS_HMC5843 tristate select IIO_BUFFER diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 7fd9b3fd914e..8f2205975c1b 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -27,6 +27,9 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o obj-$(CONFIG_INFINEON_TLV493D) += tlv493d.o +obj-$(CONFIG_QMC5883L) += qmc5883l.o +obj-$(CONFIG_QMC6308) += qmc6308.o + obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index c7fdb7c2f543..375023db2758 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -379,11 +379,7 @@ static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result) return -ERANGE; } - ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6); - if (ret) - return ret; - - return ret; + return regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6); } static irqreturn_t ak8974_drdy_irq(int irq, void *d) @@ -926,11 +922,8 @@ static int ak8974_probe(struct i2c_client *i2c) irq_trig, ak8974->name, ak8974); - if (ret) { - dev_err(&i2c->dev, "unable to request DRDY IRQ " - "- proceeding without IRQ\n"); + if (ret) goto no_irq; - } ak8974->drdy_irq = true; } diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index bf2551988008..32dff9cdd0f7 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -9,23 +9,25 @@ * (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved */ -#include <linux/module.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> +#include <linux/bitfield.h> #include <linux/cleanup.h> #include <linux/delay.h> -#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> #include <linux/pm.h> #include <linux/pm_runtime.h> -#include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + #include <linux/iio/buffer.h> #include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> #include <linux/iio/trigger.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> -#include <linux/regmap.h> -#include <linux/regulator/consumer.h> #include "bmc150_magn.h" @@ -245,14 +247,14 @@ static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data, return regmap_update_bits(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, BMC150_MAGN_MASK_OPMODE, - BMC150_MAGN_MODE_SLEEP << - BMC150_MAGN_SHIFT_OPMODE); + FIELD_PREP(BMC150_MAGN_MASK_OPMODE, + BMC150_MAGN_MODE_SLEEP)); case BMC150_MAGN_POWER_MODE_NORMAL: return regmap_update_bits(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, BMC150_MAGN_MASK_OPMODE, - BMC150_MAGN_MODE_NORMAL << - BMC150_MAGN_SHIFT_OPMODE); + FIELD_PREP(BMC150_MAGN_MASK_OPMODE, + BMC150_MAGN_MODE_NORMAL)); } return -EINVAL; @@ -290,7 +292,7 @@ static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val) ret = regmap_read(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, ®_val); if (ret < 0) return ret; - odr_val = (reg_val & BMC150_MAGN_MASK_ODR) >> BMC150_MAGN_SHIFT_ODR; + odr_val = FIELD_GET(BMC150_MAGN_MASK_ODR, reg_val); for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) if (bmc150_magn_samp_freq_table[i].reg_val == odr_val) { @@ -303,21 +305,17 @@ static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val) static int bmc150_magn_set_odr(struct bmc150_magn_data *data, int val) { - int ret; u8 i; for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) { - if (bmc150_magn_samp_freq_table[i].freq == val) { - ret = regmap_update_bits(data->regmap, - BMC150_MAGN_REG_OPMODE_ODR, - BMC150_MAGN_MASK_ODR, - bmc150_magn_samp_freq_table[i]. - reg_val << - BMC150_MAGN_SHIFT_ODR); - if (ret < 0) - return ret; - return 0; - } + if (bmc150_magn_samp_freq_table[i].freq != val) + continue; + + return regmap_update_bits(data->regmap, + BMC150_MAGN_REG_OPMODE_ODR, + BMC150_MAGN_MASK_ODR, + FIELD_PREP(BMC150_MAGN_MASK_ODR, + bmc150_magn_samp_freq_table[i].reg_val)); } return -EINVAL; @@ -800,7 +798,7 @@ static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig, ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY, BMC150_MAGN_MASK_DRDY_EN, - state << BMC150_MAGN_SHIFT_DRDY_EN); + FIELD_PREP(BMC150_MAGN_MASK_DRDY_EN, state)); if (ret < 0) return ret; diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c index 8af8d3528c43..27ab845fb257 100644 --- a/drivers/iio/magnetometer/bmc150_magn_spi.c +++ b/drivers/iio/magnetometer/bmc150_magn_spi.c @@ -33,9 +33,9 @@ static void bmc150_magn_spi_remove(struct spi_device *spi) } static const struct spi_device_id bmc150_magn_spi_id[] = { - {"bmc150_magn", 0}, - {"bmc156_magn", 0}, - {"bmm150_magn", 0}, + { .name = "bmc150_magn" }, + { .name = "bmc156_magn" }, + { .name = "bmm150_magn" }, { } }; MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id); diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index d8b8bcc865c3..ad18f233ee16 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -134,9 +134,8 @@ static const struct iio_chan_spec magn_3d_channels[] = { /* Channel read_raw handler */ static int magn_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct magn_3d_state *magn_state = iio_priv(indio_dev); int report_id = -1; @@ -166,8 +165,7 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, false); return -EINVAL; } - hid_sensor_power_state(&magn_state->magn_flux_attributes, - false); + hid_sensor_power_state(&magn_state->magn_flux_attributes, false); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: @@ -228,10 +226,8 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int magn_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct magn_3d_state *magn_state = iio_priv(indio_dev); int ret = 0; @@ -269,8 +265,7 @@ static const struct iio_info magn_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); @@ -291,9 +286,9 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); @@ -326,7 +321,7 @@ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, iio_val = magn_state->magn_val_addr[offset]; - if (iio_val != NULL) + if (iio_val) *iio_val = *((u32 *)raw_data); else ret = -EINVAL; @@ -367,18 +362,17 @@ static int magn_3d_parse_report(struct platform_device *pdev, return -EINVAL; } - dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n", - attr_count); + dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n", attr_count); dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n", - st->magn[0].index, - st->magn[0].report_id, - st->magn[1].index, st->magn[1].report_id, - st->magn[2].index, st->magn[2].report_id); + st->magn[0].index, + st->magn[0].report_id, + st->magn[1].index, st->magn[1].report_id, + st->magn[2].index, st->magn[2].report_id); /* Setup IIO channel array */ _channels = devm_kcalloc(&pdev->dev, attr_count, - sizeof(struct iio_chan_spec), - GFP_KERNEL); + sizeof(struct iio_chan_spec), + GFP_KERNEL); if (!_channels) { dev_err(&pdev->dev, "failed to allocate space for iio channels\n"); @@ -425,8 +419,7 @@ static int magn_3d_parse_report(struct platform_device *pdev, *channels = _channels; - dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n", - *chan_count); + dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n", *chan_count); st->magn_flux_attr.scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_COMPASS_3D, @@ -467,7 +460,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct magn_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -490,8 +483,8 @@ static int hid_magn_3d_probe(struct platform_device *pdev) magn_state->rot_attributes.sensitivity.index = -1; ret = magn_3d_parse_report(pdev, hsdev, - &channels, &chan_count, - HID_USAGE_SENSOR_COMPASS_3D, magn_state); + &channels, &chan_count, + HID_USAGE_SENSOR_COMPASS_3D, magn_state); if (ret) { dev_err(&pdev->dev, "failed to parse report\n"); return ret; @@ -506,32 +499,32 @@ static int hid_magn_3d_probe(struct platform_device *pdev) atomic_set(&magn_state->magn_flux_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &magn_state->magn_flux_attributes); + &magn_state->magn_flux_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - magn_state->callbacks.send_event = magn_3d_proc_event; magn_state->callbacks.capture_sample = magn_3d_capture_sample; magn_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D, - &magn_state->callbacks); + &magn_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes); return ret; @@ -544,8 +537,8 @@ static void hid_magn_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct magn_3d_state *magn_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes); } diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c index 6a55c1559b0d..29dee5dce2ae 100644 --- a/drivers/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -56,7 +56,6 @@ static int hmc5843_spi_probe(struct spi_device *spi) { int ret; struct regmap *regmap; - const struct spi_device_id *id = spi_get_device_id(spi); spi->mode = SPI_MODE_3; spi->max_speed_hz = 8000000; @@ -69,8 +68,7 @@ static int hmc5843_spi_probe(struct spi_device *spi) return PTR_ERR(regmap); return hmc5843_common_probe(&spi->dev, - regmap, - id->driver_data, id->name); + regmap, HMC5983_ID, "hmc5983"); } static void hmc5843_spi_remove(struct spi_device *spi) @@ -79,7 +77,7 @@ static void hmc5843_spi_remove(struct spi_device *spi) } static const struct spi_device_id hmc5843_id[] = { - { "hmc5983", HMC5983_ID }, + { .name = "hmc5983" }, { } }; MODULE_DEVICE_TABLE(spi, hmc5843_id); diff --git a/drivers/iio/magnetometer/mmc5983.c b/drivers/iio/magnetometer/mmc5983.c index 03f46e8c1611..d10a6288e147 100644 --- a/drivers/iio/magnetometer/mmc5983.c +++ b/drivers/iio/magnetometer/mmc5983.c @@ -327,7 +327,7 @@ static const struct of_device_id mmc5983_of_match[] = { MODULE_DEVICE_TABLE(of, mmc5983_of_match); static const struct i2c_device_id mmc5983_id[] = { - { "mmc5983" }, + { .name = "mmc5983" }, { } }; MODULE_DEVICE_TABLE(i2c, mmc5983_id); diff --git a/drivers/iio/magnetometer/qmc5883l.c b/drivers/iio/magnetometer/qmc5883l.c new file mode 100644 index 000000000000..c3469715dc63 --- /dev/null +++ b/drivers/iio/magnetometer/qmc5883l.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * Support for QST QMC5883L 3-Axis Magnetic Sensor on I2C bus. + * + * Copyright (C) 2026 Siratul Islam <siratul.islam@linux.dev> + * + * Datasheet available at + * <https://www.qstcorp.com/upload/pdf/202512/13-52-04%20QMC5883L%20Datasheet%20Rev.%20B.pdf> + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/time.h> +#include <linux/types.h> + +#include <asm/byteorder.h> + +#include <linux/iio/iio.h> + +#define QMC5883L_REG_X_LSB 0x00 +#define QMC5883L_REG_STATUS1 0x06 +#define QMC5883L_REG_CTRL1 0x09 +#define QMC5883L_REG_CTRL2 0x0A +#define QMC5883L_REG_SET_RESET 0x0B +#define QMC5883L_REG_ID 0x0D + +#define QMC5883L_CHIP_ID 0xFF + +#define QMC5883L_MODE_MASK GENMASK(1, 0) +#define QMC5883L_ODR_MASK GENMASK(3, 2) +#define QMC5883L_RNG_MASK GENMASK(5, 4) +#define QMC5883L_OSR_MASK GENMASK(7, 6) + +#define QMC5883L_MODE_STANDBY 0x00 +#define QMC5883L_MODE_CONT 0x01 + +#define QMC5883L_ODR_10HZ 0x00 +#define QMC5883L_ODR_50HZ 0x01 +#define QMC5883L_ODR_100HZ 0x02 +#define QMC5883L_ODR_200HZ 0x03 + +#define QMC5883L_RNG_2G 0x00 +#define QMC5883L_RNG_8G 0x01 + +#define QMC5883L_OSR_512 0x00 +#define QMC5883L_OSR_256 0x01 +#define QMC5883L_OSR_128 0x02 +#define QMC5883L_OSR_64 0x03 + +#define QMC5883L_STATUS_DRDY BIT(0) +#define QMC5883L_STATUS_OVL BIT(1) + +#define QMC5883L_SET_RESET_VAL BIT(0) +#define QMC5883L_INT_DISABLE BIT(0) +#define QMC5883L_SOFT_RESET BIT(7) + +#define QMC5883L_PORT_US 350 + +struct qmc5883l_data { + struct regmap *regmap; + /* + * Protect data->range/odr/osr. + * Protect poll and read during measurement. + */ + struct mutex mutex; + u8 range; + u8 odr; + u8 osr; +}; + +enum qmc5883l_chan { + QMC5883L_AXIS_X, + QMC5883L_AXIS_Y, + QMC5883L_AXIS_Z, +}; + +static const int qmc5883l_odr_avail[] = { 10, 50, 100, 200 }; + +static const int qmc5883l_osr_avail[] = { 512, 256, 128, 64 }; + +static const int qmc5883l_scales[][2] = { + [QMC5883L_RNG_2G] = { 0, 83333 }, + [QMC5883L_RNG_8G] = { 0, 333333 }, +}; + +static int qmc5883l_take_measurement(struct iio_dev *indio_dev, int index, + int *val) +{ + struct qmc5883l_data *data = iio_priv(indio_dev); + struct regmap *map = data->regmap; + unsigned int status; + __le16 buf[3]; + int ret; + + guard(mutex) (&data->mutex); + + /* 50ms headroom over the slowest ODR (10Hz) */ + ret = regmap_read_poll_timeout(map, QMC5883L_REG_STATUS1, + status, (status & QMC5883L_STATUS_DRDY), + 2 * USEC_PER_MSEC, 150 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = regmap_bulk_read(map, QMC5883L_REG_X_LSB, buf, sizeof(buf)); + if (ret) + return ret; + + if (status & QMC5883L_STATUS_OVL) + return -ERANGE; + + *val = (s16)le16_to_cpu(buf[index]); + + return 0; +} + +static int qmc5883l_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct qmc5883l_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = qmc5883l_take_measurement(indio_dev, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->mutex); + + *val = qmc5883l_scales[data->range][0]; + *val2 = qmc5883l_scales[data->range][1]; + + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&data->mutex); + + switch (data->odr) { + case QMC5883L_ODR_200HZ: + *val = 200; + break; + case QMC5883L_ODR_100HZ: + *val = 100; + break; + case QMC5883L_ODR_50HZ: + *val = 50; + break; + case QMC5883L_ODR_10HZ: + *val = 10; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&data->mutex); + + switch (data->osr) { + case QMC5883L_OSR_64: + *val = 64; + break; + case QMC5883L_OSR_128: + *val = 128; + break; + case QMC5883L_OSR_256: + *val = 256; + break; + case QMC5883L_OSR_512: + *val = 512; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int qmc5883l_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct qmc5883l_data *data = iio_priv(indio_dev); + u8 rng, osr, odr; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + if (val != 0) + return -EINVAL; + + if (val2 == qmc5883l_scales[QMC5883L_RNG_2G][1]) + rng = QMC5883L_RNG_2G; + else if (val2 == qmc5883l_scales[QMC5883L_RNG_8G][1]) + rng = QMC5883L_RNG_8G; + else + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_RNG_MASK, + FIELD_PREP(QMC5883L_RNG_MASK, rng)); + if (ret) + return ret; + + data->range = rng; + + return 0; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + switch (val) { + case 200: + odr = QMC5883L_ODR_200HZ; + break; + case 100: + odr = QMC5883L_ODR_100HZ; + break; + case 50: + odr = QMC5883L_ODR_50HZ; + break; + case 10: + odr = QMC5883L_ODR_10HZ; + break; + default: + return -EINVAL; + } + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_ODR_MASK, + FIELD_PREP(QMC5883L_ODR_MASK, odr)); + if (ret) + return ret; + + data->odr = odr; + + return 0; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + switch (val) { + case 64: + osr = QMC5883L_OSR_64; + break; + case 128: + osr = QMC5883L_OSR_128; + break; + case 256: + osr = QMC5883L_OSR_256; + break; + case 512: + osr = QMC5883L_OSR_512; + break; + default: + return -EINVAL; + } + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_OSR_MASK, + FIELD_PREP(QMC5883L_OSR_MASK, osr)); + if (ret) + return ret; + + data->osr = osr; + + return 0; + } + default: + return -EINVAL; + } +} + +static int qmc5883l_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 = qmc5883l_odr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc5883l_odr_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = qmc5883l_osr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc5883l_osr_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)qmc5883l_scales; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(qmc5883l_scales) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int qmc5883l_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; + } +} + +static const struct iio_info qmc5883l_info = { + .read_raw = qmc5883l_read_raw, + .write_raw = qmc5883l_write_raw, + .read_avail = qmc5883l_read_avail, + .write_raw_get_fmt = qmc5883l_write_raw_get_fmt, +}; + +static int qmc5883l_init(struct qmc5883l_data *data) +{ + struct regmap *map = data->regmap; + unsigned int reg; + int ret; + + ret = regmap_read(map, QMC5883L_REG_ID, ®); + if (ret) + return ret; + + /* Not failing because rev 1.0 had this register reserved */ + if (reg != QMC5883L_CHIP_ID) + dev_warn(regmap_get_device(map), + "Unknown chip id: 0x%02x, continuing\n", reg); + + ret = regmap_write(map, QMC5883L_REG_CTRL2, QMC5883L_SOFT_RESET); + if (ret) + return ret; + + /* Use POR completion time as a conservative bound */ + fsleep(QMC5883L_PORT_US); + + /* DRDY pin not used in this version of the driver */ + ret = regmap_write(map, QMC5883L_REG_CTRL2, QMC5883L_INT_DISABLE); + if (ret) + return ret; + + ret = regmap_write(map, QMC5883L_REG_SET_RESET, QMC5883L_SET_RESET_VAL); + if (ret) + return ret; + + data->odr = QMC5883L_ODR_50HZ; + data->range = QMC5883L_RNG_2G; + data->osr = QMC5883L_OSR_64; + + return regmap_write(map, QMC5883L_REG_CTRL1, + FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_CONT) | + FIELD_PREP(QMC5883L_ODR_MASK, data->odr) | + FIELD_PREP(QMC5883L_RNG_MASK, data->range) | + FIELD_PREP(QMC5883L_OSR_MASK, data->osr)); +} + +static void qmc5883l_power_down_action(void *priv) +{ + struct qmc5883l_data *data = priv; + + regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_MODE_MASK, + FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_STANDBY)); +} + +static bool qmc5883l_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg <= QMC5883L_REG_STATUS1; +} + +static bool qmc5883l_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case QMC5883L_REG_CTRL1: + case QMC5883L_REG_CTRL2: + case QMC5883L_REG_SET_RESET: + return true; + default: + return false; + } +} + +static const struct regmap_config qmc5883l_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = QMC5883L_REG_ID, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = qmc5883l_volatile_reg, + .writeable_reg = qmc5883l_writable_reg, +}; + +#define QMC5883L_CHANNEL(_axis) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = QMC5883L_AXIS_##_axis, \ + .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_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + } + +static const struct iio_chan_spec qmc5883l_channels[] = { + QMC5883L_CHANNEL(X), + QMC5883L_CHANNEL(Y), + QMC5883L_CHANNEL(Z), +}; + +static int qmc5883l_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct qmc5883l_data *data; + struct iio_dev *indio_dev; + struct regmap *map; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + map = devm_regmap_init_i2c(client, &qmc5883l_regmap_config); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), + "regmap initialization failed\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable VDD regulator\n"); + + ret = devm_regulator_get_enable(dev, "vddio"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable VDDIO regulator\n"); + + /* POR completion time max per datasheet Table 7 */ + fsleep(QMC5883L_PORT_US); + + data = iio_priv(indio_dev); + data->regmap = map; + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + indio_dev->name = "qmc5883l"; + indio_dev->info = &qmc5883l_info; + indio_dev->channels = qmc5883l_channels; + indio_dev->num_channels = ARRAY_SIZE(qmc5883l_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = qmc5883l_init(data); + if (ret) + return dev_err_probe(dev, ret, "qmc5883l init failed\n"); + + ret = devm_add_action_or_reset(dev, qmc5883l_power_down_action, data); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id qmc5883l_match[] = { + { .compatible = "qstcorp,qmc5883l" }, + { } +}; +MODULE_DEVICE_TABLE(of, qmc5883l_match); + +static const struct i2c_device_id qmc5883l_id[] = { + { .name = "qmc5883l" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qmc5883l_id); + +static struct i2c_driver qmc5883l_driver = { + .driver = { + .name = "qmc5883l", + .of_match_table = qmc5883l_match, + }, + .id_table = qmc5883l_id, + .probe = qmc5883l_probe, +}; +module_i2c_driver(qmc5883l_driver); + +MODULE_DESCRIPTION("QST QMC5883L 3-Axis Magnetic Sensor driver"); +MODULE_AUTHOR("Siratul Islam <siratul.islam@linux.dev>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/iio/magnetometer/qmc6308.c b/drivers/iio/magnetometer/qmc6308.c new file mode 100644 index 000000000000..72348cd9ef01 --- /dev/null +++ b/drivers/iio/magnetometer/qmc6308.c @@ -0,0 +1,601 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Support for QST QMC6308 3-Axis Magnetic Sensor on I2C bus. + * + * Copyright (C) 2026 Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net> + * + * Datasheet available at + * <https://qstcorp.com/upload/pdf/202202/13-52-15%20QMC6308%20Datasheet%20Rev.%20F(1).pdf> + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/time.h> +#include <linux/types.h> + +#include <asm/byteorder.h> + +#include <linux/iio/iio.h> + +#define QMC6308_REG_ID 0x00 +#define QMC6308_REG_X_LSB 0x01 +#define QMC6308_REG_STATUS 0x09 +#define QMC6308_REG_CTRL1 0x0A +#define QMC6308_REG_CTRL2 0x0B +#define QMC6308_REG_CTRL3 0x0D +#define QMC6308_REG_CTRL4 0x29 + +#define QMC6308_CHIP_ID 0x80 + +/* Control register 1 */ +#define QMC6308_MODE_MASK GENMASK(1, 0) +#define QMC6308_ODR_MASK GENMASK(3, 2) +#define QMC6308_OSR1_MASK GENMASK(5, 4) +#define QMC6308_OSR2_MASK GENMASK(7, 6) + +#define QMC6308_MODE_SUSPEND 0x00 +#define QMC6308_MODE_NORMAL 0x01 + +#define QMC6308_ODR_10HZ 0x00 +#define QMC6308_ODR_50HZ 0x01 +#define QMC6308_ODR_100HZ 0x02 +#define QMC6308_ODR_200HZ 0x03 + +#define QMC6308_OSR1_8 0x00 +#define QMC6308_OSR1_4 0x01 +#define QMC6308_OSR1_2 0x02 +#define QMC6308_OSR1_1 0x03 + +/* Control register 2 */ +#define QMC6308_SET_RESET_MASK GENMASK(1, 0) +#define QMC6308_RNG_MASK GENMASK(3, 2) +#define QMC6308_SELF_TEST BIT(6) +#define QMC6308_SOFT_RST BIT(7) + +#define QMC6308_SET_RESET_ON 0x00 + +#define QMC6308_RNG_30G 0x00 +#define QMC6308_RNG_12G 0x01 +#define QMC6308_RNG_8G 0x02 +#define QMC6308_RNG_2G 0x03 + +/* Status register */ +#define QMC6308_STATUS_DRDY BIT(0) +#define QMC6308_STATUS_OVFL BIT(1) + +/* Power-on completion time (datasheet Table 7) */ +#define QMC6308_POR_US 250 + +#define QMC6308_AUTOSUSPEND_DELAY_MS 500 + +struct qmc6308_data { + struct regmap *regmap; + /* Protect data->range/odr/osr and serialize measurements */ + struct mutex mutex; + struct iio_mount_matrix orientation; + u8 range; + u8 odr; + u8 osr; +}; + +enum qmc6308_axis { + QMC6308_AXIS_X, + QMC6308_AXIS_Y, + QMC6308_AXIS_Z, +}; + +static const int qmc6308_odr_avail[] = { + [QMC6308_ODR_10HZ] = 10, + [QMC6308_ODR_50HZ] = 50, + [QMC6308_ODR_100HZ] = 100, + [QMC6308_ODR_200HZ] = 200, +}; + +static const int qmc6308_osr1_avail[] = { + [QMC6308_OSR1_8] = 8, + [QMC6308_OSR1_4] = 4, + [QMC6308_OSR1_2] = 2, + [QMC6308_OSR1_1] = 1, +}; + +/* + * Sensitivity is 1000/2500/3750/15000 LSB/Gauss for the + * +-30/12/8/2 Gauss ranges respectively. + */ +static const int qmc6308_scales[][2] = { + [QMC6308_RNG_30G] = { 0, 1000000 }, + [QMC6308_RNG_12G] = { 0, 400000 }, + [QMC6308_RNG_8G] = { 0, 266667 }, + [QMC6308_RNG_2G] = { 0, 66667 }, +}; + +static int qmc6308_set_mode(struct qmc6308_data *data, unsigned int mode) +{ + return regmap_update_bits(data->regmap, QMC6308_REG_CTRL1, + QMC6308_MODE_MASK, + FIELD_PREP(QMC6308_MODE_MASK, mode)); +} + +static int qmc6308_take_measurement(struct iio_dev *indio_dev, int index, + int *val) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + unsigned int status; + __le16 buf[3]; + int ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(dev, "Failed to power on (%d)\n", ret); + return ret; + } + + guard(mutex)(&data->mutex); + + /* + * Reading the status register clears DRDY, which is why the poll + * and the data read stay under one mutex hold. A runtime resume + * clears DRDY too, so a sample converted before the last suspend + * is never returned here. + * + * The timeout is 50ms of headroom over the slowest ODR (10Hz). + */ + ret = regmap_read_poll_timeout(data->regmap, QMC6308_REG_STATUS, + status, (status & QMC6308_STATUS_DRDY), + 2 * USEC_PER_MSEC, + 150 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, QMC6308_REG_X_LSB, buf, + sizeof(buf)); + if (ret) + return ret; + + if (status & QMC6308_STATUS_OVFL) + return -ERANGE; + + *val = (s16)le16_to_cpu(buf[index]); + + return 0; +} + +static int qmc6308_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = qmc6308_take_measurement(indio_dev, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->mutex); + + *val = qmc6308_scales[data->range][0]; + *val2 = qmc6308_scales[data->range][1]; + + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&data->mutex); + + *val = qmc6308_odr_avail[data->odr]; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&data->mutex); + + *val = qmc6308_osr1_avail[data->osr]; + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int qmc6308_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + unsigned int status; + unsigned int i; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + if (val != 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(qmc6308_scales); i++) { + if (val2 == qmc6308_scales[i][1]) + break; + } + if (i == ARRAY_SIZE(qmc6308_scales)) + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL2, + QMC6308_RNG_MASK, + FIELD_PREP(QMC6308_RNG_MASK, i)); + if (ret) + return ret; + + data->range = i; + + /* + * The data registers still hold (and DRDY still + * advertises) a sample converted at the previous range; + * discard it so that a read does not pair old-range data + * with the new scale. A conversion already in flight may + * still complete at the old range, so this narrows the + * window rather than closing it. The range change itself + * took effect, so only log a failure here: an error + * would mislead userspace about an effective write. + */ + ret = regmap_read(data->regmap, QMC6308_REG_STATUS, + &status); + if (ret) + dev_warn(regmap_get_device(data->regmap), + "Failed to discard stale sample (%d)\n", ret); + + return 0; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + for (i = 0; i < ARRAY_SIZE(qmc6308_odr_avail); i++) { + if (val == qmc6308_odr_avail[i]) + break; + } + if (i == ARRAY_SIZE(qmc6308_odr_avail)) + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1, + QMC6308_ODR_MASK, + FIELD_PREP(QMC6308_ODR_MASK, i)); + if (ret) + return ret; + + data->odr = i; + + return 0; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + for (i = 0; i < ARRAY_SIZE(qmc6308_osr1_avail); i++) { + if (val == qmc6308_osr1_avail[i]) + break; + } + if (i == ARRAY_SIZE(qmc6308_osr1_avail)) + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1, + QMC6308_OSR1_MASK, + FIELD_PREP(QMC6308_OSR1_MASK, i)); + if (ret) + return ret; + + data->osr = i; + + return 0; + } + default: + return -EINVAL; + } +} + +static int qmc6308_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 = qmc6308_odr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc6308_odr_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = qmc6308_osr1_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc6308_osr1_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)qmc6308_scales; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(qmc6308_scales) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int qmc6308_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; + } +} + +static const struct iio_mount_matrix * +qmc6308_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info qmc6308_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, qmc6308_get_mount_matrix), + { } +}; + +static const struct iio_info qmc6308_info = { + .read_raw = qmc6308_read_raw, + .write_raw = qmc6308_write_raw, + .read_avail = qmc6308_read_avail, + .write_raw_get_fmt = qmc6308_write_raw_get_fmt, +}; + +static int qmc6308_init(struct qmc6308_data *data) +{ + struct regmap *map = data->regmap; + unsigned int reg; + int ret; + + ret = regmap_read(map, QMC6308_REG_ID, ®); + if (ret) + return ret; + + /* Allow unknown IDs so that fallback compatibles work */ + if (reg != QMC6308_CHIP_ID) + dev_warn(regmap_get_device(map), + "Unknown chip id: 0x%02x, continuing\n", reg); + + /* The SOFT_RST bit is not auto-cleared and must be written back 0 */ + ret = regmap_write(map, QMC6308_REG_CTRL2, QMC6308_SOFT_RST); + if (ret) + return ret; + + /* + * The datasheet gives no soft-reset completion figure; reuse the + * power-on time as a conservative bound. + */ + fsleep(QMC6308_POR_US); + + data->range = QMC6308_RNG_30G; + data->odr = QMC6308_ODR_50HZ; + data->osr = QMC6308_OSR1_8; + + ret = regmap_write(map, QMC6308_REG_CTRL2, + FIELD_PREP(QMC6308_SET_RESET_MASK, + QMC6308_SET_RESET_ON) | + FIELD_PREP(QMC6308_RNG_MASK, data->range)); + if (ret) + return ret; + + /* OSR2 (second-stage filter) set to its power-on default of 0 */ + return regmap_write(map, QMC6308_REG_CTRL1, + FIELD_PREP(QMC6308_MODE_MASK, + QMC6308_MODE_NORMAL) | + FIELD_PREP(QMC6308_ODR_MASK, data->odr) | + FIELD_PREP(QMC6308_OSR1_MASK, data->osr) | + FIELD_PREP(QMC6308_OSR2_MASK, 0)); +} + +static void qmc6308_power_down_action(void *priv) +{ + struct qmc6308_data *data = priv; + + if (!pm_runtime_status_suspended(regmap_get_device(data->regmap))) + qmc6308_set_mode(data, QMC6308_MODE_SUSPEND); +} + +static bool qmc6308_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg >= QMC6308_REG_X_LSB && reg <= QMC6308_REG_STATUS; +} + +static bool qmc6308_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case QMC6308_REG_CTRL1: + case QMC6308_REG_CTRL2: + case QMC6308_REG_CTRL3: + case QMC6308_REG_CTRL4: + return true; + default: + return false; + } +} + +static const struct regmap_config qmc6308_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = QMC6308_REG_CTRL4, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = qmc6308_volatile_reg, + .writeable_reg = qmc6308_writable_reg, +}; + +#define QMC6308_CHANNEL(_axis) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = QMC6308_AXIS_##_axis, \ + .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_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .ext_info = qmc6308_ext_info, \ + } + +static const struct iio_chan_spec qmc6308_channels[] = { + QMC6308_CHANNEL(X), + QMC6308_CHANNEL(Y), + QMC6308_CHANNEL(Z), +}; + +static int qmc6308_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct qmc6308_data *data; + struct iio_dev *indio_dev; + struct regmap *map; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + i2c_set_clientdata(client, indio_dev); + + map = devm_regmap_init_i2c(client, &qmc6308_regmap_config); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), + "regmap initialization failed\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable VDD regulator\n"); + + fsleep(QMC6308_POR_US); + + data = iio_priv(indio_dev); + data->regmap = map; + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + ret = iio_read_mount_matrix(dev, &data->orientation); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read mount matrix\n"); + + indio_dev->name = "qmc6308"; + indio_dev->info = &qmc6308_info; + indio_dev->channels = qmc6308_channels; + indio_dev->num_channels = ARRAY_SIZE(qmc6308_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = qmc6308_init(data); + if (ret) + return dev_err_probe(dev, ret, "qmc6308 init failed\n"); + + ret = pm_runtime_set_active(dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, qmc6308_power_down_action, data); + if (ret) + return ret; + + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, QMC6308_AUTOSUSPEND_DELAY_MS); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static int qmc6308_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct qmc6308_data *data = iio_priv(indio_dev); + + return qmc6308_set_mode(data, QMC6308_MODE_SUSPEND); +} + +static int qmc6308_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct qmc6308_data *data = iio_priv(indio_dev); + unsigned int status; + int ret; + + ret = qmc6308_set_mode(data, QMC6308_MODE_NORMAL); + if (ret) + return ret; + + /* + * DRDY may still be set for a sample converted before the last + * suspend; reading the status register clears it so the next + * measurement waits for fresh data. + */ + ret = regmap_read(data->regmap, QMC6308_REG_STATUS, &status); + if (ret) { + /* Best effort to leave the chip in a consistent state */ + qmc6308_set_mode(data, QMC6308_MODE_SUSPEND); + } + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(qmc6308_pm_ops, qmc6308_runtime_suspend, + qmc6308_runtime_resume, NULL); + +static const struct of_device_id qmc6308_match[] = { + { .compatible = "qstcorp,qmc6308" }, + { } +}; +MODULE_DEVICE_TABLE(of, qmc6308_match); + +static const struct i2c_device_id qmc6308_id[] = { + { .name = "qmc6308" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qmc6308_id); + +static struct i2c_driver qmc6308_driver = { + .driver = { + .name = "qmc6308", + .of_match_table = qmc6308_match, + .pm = pm_ptr(&qmc6308_pm_ops), + }, + .id_table = qmc6308_id, + .probe = qmc6308_probe, +}; +module_i2c_driver(qmc6308_driver); + +MODULE_DESCRIPTION("QST QMC6308 3-Axis Magnetic Sensor driver"); +MODULE_AUTHOR("Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index ac3f9f7fc808..46fce2978d76 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -568,10 +568,8 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq) IRQF_ONESHOT, indio_dev->name, indio_dev); - if (ret < 0) { - dev_err(dev, "request irq line failed.\n"); + if (ret) return ret; - } data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d", indio_dev->name, diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 69693e836ad7..af4642b6c3c0 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -87,12 +87,12 @@ static int st_magn_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_magn_id_table[] = { - { LIS3MDL_MAGN_DEV_NAME }, - { LSM303AGR_MAGN_DEV_NAME }, - { LIS2MDL_MAGN_DEV_NAME }, - { LSM9DS1_MAGN_DEV_NAME }, - { IIS2MDC_MAGN_DEV_NAME }, - { LSM303C_MAGN_DEV_NAME }, + { .name = LIS3MDL_MAGN_DEV_NAME }, + { .name = LSM303AGR_MAGN_DEV_NAME }, + { .name = LIS2MDL_MAGN_DEV_NAME }, + { .name = LSM9DS1_MAGN_DEV_NAME }, + { .name = IIS2MDC_MAGN_DEV_NAME }, + { .name = LSM303C_MAGN_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_magn_id_table); diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index 4421dafcf94e..562457a67382 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -463,3 +463,4 @@ module_platform_driver(mux_driver); MODULE_DESCRIPTION("IIO multiplexer driver"); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index ea60611192f3..aea28321db95 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -85,8 +85,7 @@ static const struct iio_chan_spec incl_3d_channels[] = { }; /* Adjust channel real bits based on report descriptor */ -static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, - int size) +static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, int size) { chan->scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -97,9 +96,8 @@ static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, /* Channel read_raw handler */ static int incl_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct incl_3d_state *incl_state = iio_priv(indio_dev); int report_id = -1; @@ -124,7 +122,7 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, min < 0); else { hid_sensor_power_state(&incl_state->common_attributes, - false); + false); return -EINVAL; } hid_sensor_power_state(&incl_state->common_attributes, false); @@ -157,10 +155,8 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int incl_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct incl_3d_state *incl_state = iio_priv(indio_dev); int ret; @@ -188,8 +184,7 @@ static const struct iio_info incl_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); @@ -211,9 +206,9 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); @@ -246,7 +241,7 @@ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev, static int incl_3d_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned usage_id, + u32 usage_id, struct incl_3d_state *st) { int ret; @@ -306,7 +301,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct incl_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -349,33 +344,33 @@ static int hid_incl_3d_probe(struct platform_device *pdev) atomic_set(&incl_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &incl_state->common_attributes); + &incl_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - incl_state->callbacks.send_event = incl_3d_proc_event; incl_state->callbacks.capture_sample = incl_3d_capture_sample; incl_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, - HID_USAGE_SENSOR_INCLINOMETER_3D, - &incl_state->callbacks); + HID_USAGE_SENSOR_INCLINOMETER_3D, + &incl_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return 0; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); return ret; @@ -388,8 +383,8 @@ static void hid_incl_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct incl_3d_state *incl_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); } diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index cc3e66dbb90f..f8c98f866cb0 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -80,9 +80,8 @@ static const struct iio_chan_spec dev_rot_channels[] = { /* Channel read_raw handler */ static int dev_rot_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int size, int *vals, int *val_len, - long mask) + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev; @@ -164,10 +163,8 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int dev_rot_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); int ret; @@ -211,8 +208,7 @@ static const struct iio_info dev_rot_info = { /* Callback handler to send event after all samples are received and captured */ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct dev_rot_state *rot_state = iio_priv(indio_dev); @@ -244,9 +240,9 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct dev_rot_state *rot_state = iio_priv(indio_dev); @@ -275,7 +271,7 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int dev_rot_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, struct dev_rot_state *st) { int ret; @@ -313,7 +309,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct dev_rot_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -361,32 +357,32 @@ static int hid_dev_rot_probe(struct platform_device *pdev) atomic_set(&rot_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &rot_state->common_attributes); + &rot_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - rot_state->callbacks.send_event = dev_rot_proc_event; rot_state->callbacks.capture_sample = dev_rot_capture_sample; rot_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, - &rot_state->callbacks); + &rot_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return 0; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes); return ret; @@ -399,8 +395,8 @@ static void hid_dev_rot_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct dev_rot_state *rot_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes); } diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index bffa8fe4fe44..d275bc1413fe 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -106,8 +106,8 @@ static void hinge_adjust_channel_realbits(struct iio_chan_spec *channels, /* Channel read_raw handler */ static int hinge_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct hinge_state *st = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev; @@ -153,8 +153,8 @@ static int hinge_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int hinge_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct hinge_state *st = iio_priv(indio_dev); @@ -189,7 +189,7 @@ static const struct iio_info hinge_info = { * and captured. */ static int hinge_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct hinge_state *st = iio_priv(indio_dev); @@ -208,8 +208,9 @@ static int hinge_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, size_t raw_len, - char *raw_data, void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct hinge_state *st = iio_priv(indio_dev); @@ -235,7 +236,7 @@ static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev, static int hinge_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned int usage_id, struct hinge_state *st) + u32 usage_id, struct hinge_state *st) { int ret; int i; @@ -291,7 +292,7 @@ static int hid_hinge_probe(struct platform_device *pdev) } indio_dev->num_channels = ARRAY_SIZE(hinge_channels); - indio_dev->channels = devm_kmemdup(&indio_dev->dev, hinge_channels, + indio_dev->channels = devm_kmemdup(&pdev->dev, hinge_channels, sizeof(hinge_channels), GFP_KERNEL); if (!indio_dev->channels) return -ENOMEM; diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c index ddf54a1df976..ec1b7bdc706b 100644 --- a/drivers/iio/potentiometer/max5481.c +++ b/drivers/iio/potentiometer/max5481.c @@ -169,10 +169,10 @@ static int max5481_probe(struct spi_device *spi) } static const struct spi_device_id max5481_id_table[] = { - { "max5481", max5481 }, - { "max5482", max5482 }, - { "max5483", max5483 }, - { "max5484", max5484 }, + { .name = "max5481", .driver_data = max5481 }, + { .name = "max5482", .driver_data = max5482 }, + { .name = "max5483", .driver_data = max5483 }, + { .name = "max5484", .driver_data = max5484 }, { } }; MODULE_DEVICE_TABLE(spi, max5481_id_table); diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c index 9541695af474..1f0d0056f412 100644 --- a/drivers/iio/potentiometer/max5487.c +++ b/drivers/iio/potentiometer/max5487.c @@ -125,9 +125,9 @@ static void max5487_spi_remove(struct spi_device *spi) } static const struct spi_device_id max5487_id[] = { - { "MAX5487", 10 }, - { "MAX5488", 50 }, - { "MAX5489", 100 }, + { .name = "MAX5487", .driver_data = 10 }, + { .name = "MAX5488", .driver_data = 50 }, + { .name = "MAX5489", .driver_data = 100 }, { } }; MODULE_DEVICE_TABLE(spi, max5487_id); diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c index ed0764231224..cfd5d0f6d368 100644 --- a/drivers/iio/potentiometer/mcp41010.c +++ b/drivers/iio/potentiometer/mcp41010.c @@ -175,12 +175,12 @@ static const struct of_device_id mcp41010_match[] = { MODULE_DEVICE_TABLE(of, mcp41010_match); static const struct spi_device_id mcp41010_id[] = { - { "mcp41010", MCP41010 }, - { "mcp41050", MCP41050 }, - { "mcp41100", MCP41100 }, - { "mcp42010", MCP42010 }, - { "mcp42050", MCP42050 }, - { "mcp42100", MCP42100 }, + { .name = "mcp41010", .driver_data = MCP41010 }, + { .name = "mcp41050", .driver_data = MCP41050 }, + { .name = "mcp41100", .driver_data = MCP41100 }, + { .name = "mcp42010", .driver_data = MCP42010 }, + { .name = "mcp42050", .driver_data = MCP42050 }, + { .name = "mcp42100", .driver_data = MCP42100 }, { } }; MODULE_DEVICE_TABLE(spi, mcp41010_id); diff --git a/drivers/iio/potentiometer/mcp4131.c b/drivers/iio/potentiometer/mcp4131.c index dad09ce6a75d..fadf9fcc1817 100644 --- a/drivers/iio/potentiometer/mcp4131.c +++ b/drivers/iio/potentiometer/mcp4131.c @@ -406,70 +406,70 @@ static const struct of_device_id mcp4131_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp4131_dt_ids); static const struct spi_device_id mcp4131_id[] = { - { "mcp4131-502", MCP413x_502 }, - { "mcp4131-103", MCP413x_103 }, - { "mcp4131-503", MCP413x_503 }, - { "mcp4131-104", MCP413x_104 }, - { "mcp4132-502", MCP413x_502 }, - { "mcp4132-103", MCP413x_103 }, - { "mcp4132-503", MCP413x_503 }, - { "mcp4132-104", MCP413x_104 }, - { "mcp4141-502", MCP414x_502 }, - { "mcp4141-103", MCP414x_103 }, - { "mcp4141-503", MCP414x_503 }, - { "mcp4141-104", MCP414x_104 }, - { "mcp4142-502", MCP414x_502 }, - { "mcp4142-103", MCP414x_103 }, - { "mcp4142-503", MCP414x_503 }, - { "mcp4142-104", MCP414x_104 }, - { "mcp4151-502", MCP415x_502 }, - { "mcp4151-103", MCP415x_103 }, - { "mcp4151-503", MCP415x_503 }, - { "mcp4151-104", MCP415x_104 }, - { "mcp4152-502", MCP415x_502 }, - { "mcp4152-103", MCP415x_103 }, - { "mcp4152-503", MCP415x_503 }, - { "mcp4152-104", MCP415x_104 }, - { "mcp4161-502", MCP416x_502 }, - { "mcp4161-103", MCP416x_103 }, - { "mcp4161-503", MCP416x_503 }, - { "mcp4161-104", MCP416x_104 }, - { "mcp4162-502", MCP416x_502 }, - { "mcp4162-103", MCP416x_103 }, - { "mcp4162-503", MCP416x_503 }, - { "mcp4162-104", MCP416x_104 }, - { "mcp4231-502", MCP423x_502 }, - { "mcp4231-103", MCP423x_103 }, - { "mcp4231-503", MCP423x_503 }, - { "mcp4231-104", MCP423x_104 }, - { "mcp4232-502", MCP423x_502 }, - { "mcp4232-103", MCP423x_103 }, - { "mcp4232-503", MCP423x_503 }, - { "mcp4232-104", MCP423x_104 }, - { "mcp4241-502", MCP424x_502 }, - { "mcp4241-103", MCP424x_103 }, - { "mcp4241-503", MCP424x_503 }, - { "mcp4241-104", MCP424x_104 }, - { "mcp4242-502", MCP424x_502 }, - { "mcp4242-103", MCP424x_103 }, - { "mcp4242-503", MCP424x_503 }, - { "mcp4242-104", MCP424x_104 }, - { "mcp4251-502", MCP425x_502 }, - { "mcp4251-103", MCP425x_103 }, - { "mcp4251-503", MCP425x_503 }, - { "mcp4251-104", MCP425x_104 }, - { "mcp4252-502", MCP425x_502 }, - { "mcp4252-103", MCP425x_103 }, - { "mcp4252-503", MCP425x_503 }, - { "mcp4252-104", MCP425x_104 }, - { "mcp4261-502", MCP426x_502 }, - { "mcp4261-103", MCP426x_103 }, - { "mcp4261-503", MCP426x_503 }, - { "mcp4261-104", MCP426x_104 }, - { "mcp4262-502", MCP426x_502 }, - { "mcp4262-103", MCP426x_103 }, - { "mcp4262-503", MCP426x_503 }, - { "mcp4262-104", MCP426x_104 }, + { .name = "mcp4131-502", .driver_data = MCP413x_502 }, + { .name = "mcp4131-103", .driver_data = MCP413x_103 }, + { .name = "mcp4131-503", .driver_data = MCP413x_503 }, + { .name = "mcp4131-104", .driver_data = MCP413x_104 }, + { .name = "mcp4132-502", .driver_data = MCP413x_502 }, + { .name = "mcp4132-103", .driver_data = MCP413x_103 }, + { .name = "mcp4132-503", .driver_data = MCP413x_503 }, + { .name = "mcp4132-104", .driver_data = MCP413x_104 }, + { .name = "mcp4141-502", .driver_data = MCP414x_502 }, + { .name = "mcp4141-103", .driver_data = MCP414x_103 }, + { .name = "mcp4141-503", .driver_data = MCP414x_503 }, + { .name = "mcp4141-104", .driver_data = MCP414x_104 }, + { .name = "mcp4142-502", .driver_data = MCP414x_502 }, + { .name = "mcp4142-103", .driver_data = MCP414x_103 }, + { .name = "mcp4142-503", .driver_data = MCP414x_503 }, + { .name = "mcp4142-104", .driver_data = MCP414x_104 }, + { .name = "mcp4151-502", .driver_data = MCP415x_502 }, + { .name = "mcp4151-103", .driver_data = MCP415x_103 }, + { .name = "mcp4151-503", .driver_data = MCP415x_503 }, + { .name = "mcp4151-104", .driver_data = MCP415x_104 }, + { .name = "mcp4152-502", .driver_data = MCP415x_502 }, + { .name = "mcp4152-103", .driver_data = MCP415x_103 }, + { .name = "mcp4152-503", .driver_data = MCP415x_503 }, + { .name = "mcp4152-104", .driver_data = MCP415x_104 }, + { .name = "mcp4161-502", .driver_data = MCP416x_502 }, + { .name = "mcp4161-103", .driver_data = MCP416x_103 }, + { .name = "mcp4161-503", .driver_data = MCP416x_503 }, + { .name = "mcp4161-104", .driver_data = MCP416x_104 }, + { .name = "mcp4162-502", .driver_data = MCP416x_502 }, + { .name = "mcp4162-103", .driver_data = MCP416x_103 }, + { .name = "mcp4162-503", .driver_data = MCP416x_503 }, + { .name = "mcp4162-104", .driver_data = MCP416x_104 }, + { .name = "mcp4231-502", .driver_data = MCP423x_502 }, + { .name = "mcp4231-103", .driver_data = MCP423x_103 }, + { .name = "mcp4231-503", .driver_data = MCP423x_503 }, + { .name = "mcp4231-104", .driver_data = MCP423x_104 }, + { .name = "mcp4232-502", .driver_data = MCP423x_502 }, + { .name = "mcp4232-103", .driver_data = MCP423x_103 }, + { .name = "mcp4232-503", .driver_data = MCP423x_503 }, + { .name = "mcp4232-104", .driver_data = MCP423x_104 }, + { .name = "mcp4241-502", .driver_data = MCP424x_502 }, + { .name = "mcp4241-103", .driver_data = MCP424x_103 }, + { .name = "mcp4241-503", .driver_data = MCP424x_503 }, + { .name = "mcp4241-104", .driver_data = MCP424x_104 }, + { .name = "mcp4242-502", .driver_data = MCP424x_502 }, + { .name = "mcp4242-103", .driver_data = MCP424x_103 }, + { .name = "mcp4242-503", .driver_data = MCP424x_503 }, + { .name = "mcp4242-104", .driver_data = MCP424x_104 }, + { .name = "mcp4251-502", .driver_data = MCP425x_502 }, + { .name = "mcp4251-103", .driver_data = MCP425x_103 }, + { .name = "mcp4251-503", .driver_data = MCP425x_503 }, + { .name = "mcp4251-104", .driver_data = MCP425x_104 }, + { .name = "mcp4252-502", .driver_data = MCP425x_502 }, + { .name = "mcp4252-103", .driver_data = MCP425x_103 }, + { .name = "mcp4252-503", .driver_data = MCP425x_503 }, + { .name = "mcp4252-104", .driver_data = MCP425x_104 }, + { .name = "mcp4261-502", .driver_data = MCP426x_502 }, + { .name = "mcp4261-103", .driver_data = MCP426x_103 }, + { .name = "mcp4261-503", .driver_data = MCP426x_503 }, + { .name = "mcp4261-104", .driver_data = MCP426x_104 }, + { .name = "mcp4262-502", .driver_data = MCP426x_502 }, + { .name = "mcp4262-103", .driver_data = MCP426x_103 }, + { .name = "mcp4262-503", .driver_data = MCP426x_503 }, + { .name = "mcp4262-104", .driver_data = MCP426x_104 }, { } }; MODULE_DEVICE_TABLE(spi, mcp4131_id); diff --git a/drivers/iio/potentiometer/x9250.c b/drivers/iio/potentiometer/x9250.c index 735348492699..974e4447fc2c 100644 --- a/drivers/iio/potentiometer/x9250.c +++ b/drivers/iio/potentiometer/x9250.c @@ -198,8 +198,8 @@ static const struct of_device_id x9250_of_match[] = { MODULE_DEVICE_TABLE(of, x9250_of_match); static const struct spi_device_id x9250_id_table[] = { - { "x9250t", (kernel_ulong_t)&x9250_cfg[X9250T] }, - { "x9250u", (kernel_ulong_t)&x9250_cfg[X9250U] }, + { .name = "x9250t", .driver_data = (kernel_ulong_t)&x9250_cfg[X9250T] }, + { .name = "x9250u", .driver_data = (kernel_ulong_t)&x9250_cfg[X9250U] }, { } }; MODULE_DEVICE_TABLE(spi, x9250_id_table); diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index 1984d990438c..00cb3fea97ca 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -422,3 +422,4 @@ module_i2c_driver(lmp91000_driver); MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); MODULE_DESCRIPTION("LMP91000 digital potentiostat"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/pressure/abp2030pa.c b/drivers/iio/pressure/abp2030pa.c index b44f1bf4c633..41395036e9ad 100644 --- a/drivers/iio/pressure/abp2030pa.c +++ b/drivers/iio/pressure/abp2030pa.c @@ -379,7 +379,6 @@ static int abp2_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } - return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (channel->type) { diff --git a/drivers/iio/pressure/abp2030pa_spi.c b/drivers/iio/pressure/abp2030pa_spi.c index 8bc59eb499aa..60d078574827 100644 --- a/drivers/iio/pressure/abp2030pa_spi.c +++ b/drivers/iio/pressure/abp2030pa_spi.c @@ -45,7 +45,7 @@ static const struct of_device_id abp2_spi_match[] = { MODULE_DEVICE_TABLE(of, abp2_spi_match); static const struct spi_device_id abp2_spi_id[] = { - { "abp2030pa" }, + { .name = "abp2030pa" }, { } }; MODULE_DEVICE_TABLE(spi, abp2_spi_id); diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index dbe42233c81d..3940b8e61a06 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -750,7 +750,10 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, struct bmp280_data *data = iio_priv(indio_dev); int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp280_read_raw_impl(indio_dev, chan, val, val2, mask); pm_runtime_put_autosuspend(data->dev); @@ -924,7 +927,10 @@ static int bmp280_write_raw(struct iio_dev *indio_dev, struct bmp280_data *data = iio_priv(indio_dev); int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp280_write_raw_impl(indio_dev, chan, val, val2, mask); pm_runtime_put_autosuspend(data->dev); @@ -1333,7 +1339,7 @@ static int __bmp280_trigger_probe(struct iio_dev *indio_dev, irq_thread_handler, IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(dev, ret, "request IRQ failed.\n"); + return ret; ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) @@ -2254,7 +2260,10 @@ static int bmp580_nvmem_read(void *priv, unsigned int offset, void *val, struct bmp280_data *data = priv; int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp580_nvmem_read_impl(priv, offset, val, bytes); pm_runtime_put_autosuspend(data->dev); @@ -2328,7 +2337,10 @@ static int bmp580_nvmem_write(void *priv, unsigned int offset, void *val, struct bmp280_data *data = priv; int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp580_nvmem_write_impl(priv, offset, val, bytes); pm_runtime_put_autosuspend(data->dev); @@ -3108,11 +3120,17 @@ EXPORT_SYMBOL_NS(bmp085_chip_info, "IIO_BMP280"); static int bmp280_buffer_preenable(struct iio_dev *indio_dev) { struct bmp280_data *data = iio_priv(indio_dev); + int ret; - pm_runtime_get_sync(data->dev); - data->chip_info->set_mode(data, BMP280_NORMAL); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; - return 0; + ret = data->chip_info->set_mode(data, BMP280_NORMAL); + if (ret) + pm_runtime_put_autosuspend(data->dev); + + return ret; } static int bmp280_buffer_postdisable(struct iio_dev *indio_dev) diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 04bf2f5be5b1..a02a621b8015 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -118,13 +118,13 @@ static const struct of_device_id bmp280_of_spi_match[] = { MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); static const struct spi_device_id bmp280_spi_id[] = { - { "bmp085", (kernel_ulong_t)&bmp085_chip_info }, - { "bmp180", (kernel_ulong_t)&bmp180_chip_info }, - { "bmp181", (kernel_ulong_t)&bmp180_chip_info }, - { "bmp280", (kernel_ulong_t)&bmp280_chip_info }, - { "bme280", (kernel_ulong_t)&bme280_chip_info }, - { "bmp380", (kernel_ulong_t)&bmp380_chip_info }, - { "bmp580", (kernel_ulong_t)&bmp580_chip_info }, + { .name = "bmp085", .driver_data = (kernel_ulong_t)&bmp085_chip_info }, + { .name = "bmp180", .driver_data = (kernel_ulong_t)&bmp180_chip_info }, + { .name = "bmp181", .driver_data = (kernel_ulong_t)&bmp180_chip_info }, + { .name = "bmp280", .driver_data = (kernel_ulong_t)&bmp280_chip_info }, + { .name = "bme280", .driver_data = (kernel_ulong_t)&bme280_chip_info }, + { .name = "bmp380", .driver_data = (kernel_ulong_t)&bmp380_chip_info }, + { .name = "bmp580", .driver_data = (kernel_ulong_t)&bmp580_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, bmp280_spi_id); diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c index 01a873165923..961888fd03a7 100644 --- a/drivers/iio/pressure/dlhl60d.c +++ b/drivers/iio/pressure/dlhl60d.c @@ -309,10 +309,8 @@ static int dlh_probe(struct i2c_client *client) ret = devm_request_irq(&client->dev, client->irq, dlh_interrupt, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, st->info->name, indio_dev); - if (ret) { - dev_err(&client->dev, "failed to allocate threaded irq"); + if (ret) return ret; - } st->use_interrupt = true; init_completion(&st->completion); diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index ae499d197555..e688b0776547 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -55,9 +55,8 @@ static const struct iio_chan_spec press_channels[] = { /* Channel read_raw handler */ static int press_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct press_state *press_state = iio_priv(indio_dev); int report_id = -1; @@ -81,7 +80,7 @@ static int press_read_raw(struct iio_dev *indio_dev, } if (report_id >= 0) { hid_sensor_power_state(&press_state->common_attributes, - true); + true); *val = sensor_hub_input_attr_get_raw_value( press_state->common_attributes.hsdev, HID_USAGE_SENSOR_PRESSURE, address, @@ -89,7 +88,7 @@ static int press_read_raw(struct iio_dev *indio_dev, SENSOR_HUB_SYNC, min < 0); hid_sensor_power_state(&press_state->common_attributes, - false); + false); } else { *val = 0; return -EINVAL; @@ -123,10 +122,8 @@ static int press_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int press_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct press_state *press_state = iio_priv(indio_dev); int ret = 0; @@ -154,8 +151,7 @@ static const struct iio_info press_info = { /* Callback handler to send event after all samples are received and captured */ static int press_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct press_state *press_state = iio_priv(indio_dev); @@ -175,7 +171,7 @@ static int press_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int press_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { @@ -201,10 +197,10 @@ static int press_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int press_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - unsigned usage_id, - struct press_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct press_state *st) { int ret; @@ -221,7 +217,7 @@ static int press_parse_report(struct platform_device *pdev, }; dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index, - st->press_attr.report_id); + st->press_attr.report_id); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_PRESSURE, @@ -240,8 +236,7 @@ static int hid_press_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct press_state *press_state; - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct press_state)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct press_state)); if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -284,32 +279,32 @@ static int hid_press_probe(struct platform_device *pdev) atomic_set(&press_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &press_state->common_attributes); + &press_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - press_state->callbacks.send_event = press_proc_event; press_state->callbacks.capture_sample = press_capture_sample; press_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE, - &press_state->callbacks); + &press_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes); return ret; @@ -322,8 +317,8 @@ static void hid_press_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct press_state *press_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes); } diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c index be58e550cc12..02021b6f799a 100644 --- a/drivers/iio/pressure/hsc030pa_spi.c +++ b/drivers/iio/pressure/hsc030pa_spi.c @@ -39,7 +39,7 @@ static const struct of_device_id hsc_spi_match[] = { MODULE_DEVICE_TABLE(of, hsc_spi_match); static const struct spi_device_id hsc_spi_id[] = { - { "hsc030pa" }, + { .name = "hsc030pa" }, { } }; MODULE_DEVICE_TABLE(spi, hsc_spi_id); diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index 16e112b796ba..2929ea8e4e01 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -203,9 +203,9 @@ int mpl115_probe(struct device *dev, const char *name, if (data->shutdown) { /* Enable runtime PM */ - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = pm_runtime_set_active(dev); + if (ret) + return ret; /* * As the device takes 3 ms to come up with a fresh @@ -215,7 +215,10 @@ int mpl115_probe(struct device *dev, const char *name, */ pm_runtime_set_autosuspend_delay(dev, 2000); pm_runtime_use_autosuspend(dev); - pm_runtime_put(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; dev_dbg(dev, "low-power mode enabled"); } else diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c index 4e1d24beff94..3cff616e8d2f 100644 --- a/drivers/iio/pressure/mpl115_spi.c +++ b/drivers/iio/pressure/mpl115_spi.c @@ -84,7 +84,7 @@ static int mpl115_spi_probe(struct spi_device *spi) } static const struct spi_device_id mpl115_spi_ids[] = { - { "mpl115", 0 }, + { .name = "mpl115" }, { } }; MODULE_DEVICE_TABLE(spi, mpl115_spi_ids); diff --git a/drivers/iio/pressure/mprls0025pa_spi.c b/drivers/iio/pressure/mprls0025pa_spi.c index 23f47c04ed45..6270370383c3 100644 --- a/drivers/iio/pressure/mprls0025pa_spi.c +++ b/drivers/iio/pressure/mprls0025pa_spi.c @@ -59,7 +59,7 @@ static const struct of_device_id mpr_spi_match[] = { MODULE_DEVICE_TABLE(of, mpr_spi_match); static const struct spi_device_id mpr_spi_id[] = { - { "mprls0025pa" }, + { .name = "mprls0025pa" }, { } }; MODULE_DEVICE_TABLE(spi, mpr_spi_id); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index c41aaa6244cd..7133e6744e3f 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -113,8 +113,8 @@ static const struct of_device_id ms5611_spi_matches[] = { MODULE_DEVICE_TABLE(of, ms5611_spi_matches); static const struct spi_device_id ms5611_id[] = { - { "ms5611", MS5611 }, - { "ms5607", MS5607 }, + { .name = "ms5611", .driver_data = MS5611 }, + { .name = "ms5607", .driver_data = MS5607 }, { } }; MODULE_DEVICE_TABLE(spi, ms5611_id); diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index d00d7ed54cb1..f9deb48e6a41 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -621,17 +621,15 @@ static const struct iio_buffer_setup_ops bm1390_buffer_ops = { .predisable = bm1390_buffer_predisable, }; -static irqreturn_t bm1390_trigger_handler(int irq, void *p) +static bool bm1390_handle_trigger(struct iio_dev *idev) { - struct iio_poll_func *pf = p; - struct iio_dev *idev = pf->indio_dev; struct bm1390_data *data = iio_priv(idev); int ret, status; /* DRDY is acked by reading status reg */ ret = regmap_read(data->regmap, BM1390_REG_STATUS, &status); if (ret || !status) - return IRQ_NONE; + return false; dev_dbg(data->dev, "DRDY trig status 0x%x\n", status); @@ -639,7 +637,7 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p) ret = bm1390_pressure_read(data, &data->buf.pressure); if (ret) { dev_warn(data->dev, "sample read failed %d\n", ret); - return IRQ_NONE; + return false; } } @@ -648,15 +646,26 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p) &data->buf.temp, sizeof(data->buf.temp)); if (ret) { dev_warn(data->dev, "temp read failed %d\n", ret); - return IRQ_HANDLED; + return true; } } iio_push_to_buffers_with_ts(idev, &data->buf, sizeof(data->buf), data->timestamp); + + return true; +} + +static irqreturn_t bm1390_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + bool result; + + result = bm1390_handle_trigger(idev); iio_trigger_notify_done(idev->trig); - return IRQ_HANDLED; + return IRQ_RETVAL(result); } /* Get timestamps and wake the thread if we need to read data */ @@ -796,7 +805,7 @@ static int bm1390_setup_trigger(struct bm1390_data *data, struct iio_dev *idev, &bm1390_irq_thread_handler, IRQF_ONESHOT, name, idev); if (ret) - return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); + return ret; ret = devm_iio_trigger_register(data->dev, itrig); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index d843186b44af..8add0c392c6a 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -93,18 +93,18 @@ static int st_press_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_press_id_table[] = { - { LPS001WP_PRESS_DEV_NAME }, - { LPS25H_PRESS_DEV_NAME }, - { LPS331AP_PRESS_DEV_NAME }, - { LPS22HB_PRESS_DEV_NAME }, - { LPS33HW_PRESS_DEV_NAME }, - { LPS35HW_PRESS_DEV_NAME }, - { LPS22HH_PRESS_DEV_NAME }, - { LPS22DF_PRESS_DEV_NAME }, - { "lps001wp-press" }, - { "lps25h-press", }, - { "lps331ap-press" }, - { "lps22hb-press" }, + { .name = LPS001WP_PRESS_DEV_NAME }, + { .name = LPS25H_PRESS_DEV_NAME }, + { .name = LPS331AP_PRESS_DEV_NAME }, + { .name = LPS22HB_PRESS_DEV_NAME }, + { .name = LPS33HW_PRESS_DEV_NAME }, + { .name = LPS35HW_PRESS_DEV_NAME }, + { .name = LPS22HH_PRESS_DEV_NAME }, + { .name = LPS22DF_PRESS_DEV_NAME }, + { .name = "lps001wp-press" }, + { .name = "lps25h-press" }, + { .name = "lps331ap-press" }, + { .name = "lps22hb-press" }, { } }; MODULE_DEVICE_TABLE(spi, st_press_id_table); diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 2c68fdf2744e..b38493ff3b8b 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -911,11 +911,8 @@ static int zpa2326_init_managed_irq(struct device *parent, zpa2326_handle_threaded_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev_name(parent), indio_dev); - if (err) { - dev_err(parent, "failed to request interrupt %d (%d)", irq, - err); + if (err) return err; - } dev_info(parent, "using interrupt %d", irq); diff --git a/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c index 73e37a77c933..dfbb74a58dc0 100644 --- a/drivers/iio/pressure/zpa2326_spi.c +++ b/drivers/iio/pressure/zpa2326_spi.c @@ -60,7 +60,7 @@ static void zpa2326_remove_spi(struct spi_device *spi) } static const struct spi_device_id zpa2326_spi_ids[] = { - { "zpa2326", 0 }, + { .name = "zpa2326" }, { } }; MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids); diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 3406232822cb..d7f43c3af165 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -428,10 +428,8 @@ static int as3935_probe(struct spi_device *spi) dev_name(dev), indio_dev); - if (ret) { - dev_err(dev, "unable to request irq\n"); + if (ret) return ret; - } ret = devm_iio_device_register(dev, indio_dev); if (ret < 0) { @@ -448,7 +446,7 @@ static const struct of_device_id as3935_of_match[] = { MODULE_DEVICE_TABLE(of, as3935_of_match); static const struct spi_device_id as3935_id[] = { - {"as3935", 0}, + { .name = "as3935" }, { } }; MODULE_DEVICE_TABLE(spi, as3935_id); diff --git a/drivers/iio/proximity/d3323aa.c b/drivers/iio/proximity/d3323aa.c index d40e3dff9eb1..1f43c1fed342 100644 --- a/drivers/iio/proximity/d3323aa.c +++ b/drivers/iio/proximity/d3323aa.c @@ -771,7 +771,7 @@ static int d3323aa_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, dev_name(dev), indio_dev); if (ret) - return dev_err_probe(dev, ret, "Could not request IRQ\n"); + return ret; ret = d3323aa_setup(indio_dev, D3323AA_LP_FILTER_FREQ_DEFAULT_IDX, D3323AA_FILTER_GAIN_DEFAULT_IDX, diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c index a6ff7cbe9e65..0199a6351a46 100644 --- a/drivers/iio/proximity/hx9023s.c +++ b/drivers/iio/proximity/hx9023s.c @@ -1139,7 +1139,7 @@ static int hx9023s_probe(struct i2c_client *client) IRQF_ONESHOT, "hx9023s_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, "irq request failed\n"); + return ret; data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c index 65af31d43453..b3db7f3a49f6 100644 --- a/drivers/iio/proximity/irsd200.c +++ b/drivers/iio/proximity/irsd200.c @@ -910,7 +910,7 @@ static int irsd200_probe(struct i2c_client *client) IRQF_TRIGGER_RISING | IRQF_ONESHOT, NULL, indio_dev); if (ret) - return dev_err_probe(data->dev, ret, "Could not request irq\n"); + return ret; trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c index eab881b0cdc7..accfc296e1f0 100644 --- a/drivers/iio/proximity/mb1232.c +++ b/drivers/iio/proximity/mb1232.c @@ -213,10 +213,8 @@ static int mb1232_probe(struct i2c_client *client) if (data->irqnr > 0) { ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq, IRQF_TRIGGER_FALLING, id->name, indio_dev); - if (ret < 0) { - dev_err(dev, "request_irq: %d\n", ret); + if (ret) return ret; - } } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index e03f3f0dee04..4e042214df29 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -303,10 +303,8 @@ static int srf04_probe(struct platform_device *pdev) ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, pdev->name, indio_dev); - if (ret < 0) { - dev_err(data->dev, "request_irq: %d\n", ret); + if (ret) return ret; - } platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c index 59b35e40739b..4d4937395d9a 100644 --- a/drivers/iio/proximity/sx_common.c +++ b/drivers/iio/proximity/sx_common.c @@ -517,7 +517,7 @@ int sx_common_probe(struct i2c_client *client, IRQF_ONESHOT, "sx_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, "No IRQ\n"); + return ret; data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index 7f417372566a..6b0d7a9038d9 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -638,12 +638,8 @@ static int vcnl3020_probe(struct i2c_client *client) NULL, vcnl3020_handle_irq_thread, IRQF_ONESHOT, indio_dev->name, indio_dev); - if (rc) { - dev_err(&client->dev, - "Error (%d) irq request failed (%u)\n", rc, - client->irq); + if (rc) return rc; - } } return devm_iio_device_register(&client->dev, indio_dev); diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index b7127c3c52d2..9fe14ceb8be7 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -127,10 +127,8 @@ static int vl53l0x_configure_irq(struct i2c_client *client, ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, vl53l0x_threaded_irq, irq_flags | IRQF_ONESHOT, indio_dev->name, indio_dev); - if (ret) { - dev_err(&client->dev, "devm_request_irq error: %d\n", ret); + if (ret) return ret; - } ret = i2c_smbus_write_byte_data(data->client, VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c index dc4ffbc95d1f..bce238d1b6de 100644 --- a/drivers/iio/proximity/vl53l1x-i2c.c +++ b/drivers/iio/proximity/vl53l1x-i2c.c @@ -2,7 +2,7 @@ /* * Support for ST VL53L1X FlightSense ToF Ranging Sensor on a i2c bus. * - * Copyright (C) 2026 Siratul Islam <email@sirat.me> + * Copyright (C) 2026 Siratul Islam <siratul.islam@linux.dev> * * Datasheet available at * <https://www.st.com/resource/en/datasheet/vl53l1x.pdf> @@ -42,6 +42,7 @@ #define VL53L1X_REG_SOFT_RESET 0x0000 #define VL53L1X_REG_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008 #define VL53L1X_REG_VHV_CONFIG__INIT 0x000B +#define VL53L1X_REG_DEFAULT_CONFIG 0x002D #define VL53L1X_REG_GPIO_HV_MUX__CTRL 0x0030 #define VL53L1X_REG_GPIO__TIO_HV_STATUS 0x0031 #define VL53L1X_REG_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 @@ -63,7 +64,6 @@ #define VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL 0x00DE #define VL53L1X_REG_FIRMWARE__SYSTEM_STATUS 0x00E5 #define VL53L1X_REG_IDENTIFICATION__MODEL_ID 0x010F -#define VL53L1X_REG_DEFAULT_CONFIG 0x002D #define VL53L1X_MODEL_ID_VAL 0xEACC @@ -81,6 +81,9 @@ #define VL53L1X_OSC_CALIBRATE_MASK GENMASK(9, 0) +#define VL53L1X_FIRMWARE__SYSTEM_STATUS_BOOTED BIT(0) +#define VL53L1X_GPIO__TIO_HV_STATUS_DATA_READY BIT(0) + /* Inter-measurement period uses PLL divider with 1.075 oscillator correction */ static const struct u32_fract vl53l1x_osc_correction = { .numerator = 1075, @@ -190,6 +193,17 @@ static int vl53l1x_stop_ranging(struct vl53l1x_data *data) VL53L1X_MODE_START_STOP); } +static int vl53l1x_wait_data_ready(struct vl53l1x_data *data) +{ + unsigned int val; + + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ + return regmap_read_poll_timeout(data->regmap, + VL53L1X_REG_GPIO__TIO_HV_STATUS, val, + (val & VL53L1X_GPIO__TIO_HV_STATUS_DATA_READY) != data->gpio_polarity, + 1 * USEC_PER_MSEC, 1 * USEC_PER_SEC); +} + /* * Default configuration blob from ST's VL53L1X Ultra Lite Driver * (STSW-IMG009). @@ -229,10 +243,9 @@ static int vl53l1x_chip_init(struct vl53l1x_data *data) } ret = regmap_read_poll_timeout(data->regmap, - VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, val, - val & BIT(0), - 1 * USEC_PER_MSEC, - 100 * USEC_PER_MSEC); + VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, + val, val & VL53L1X_FIRMWARE__SYSTEM_STATUS_BOOTED, + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); if (ret) return dev_err_probe(dev, ret, "firmware boot timeout\n"); @@ -260,12 +273,7 @@ static int vl53l1x_chip_init(struct vl53l1x_data *data) if (ret) return ret; - /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ - ret = regmap_read_poll_timeout(data->regmap, - VL53L1X_REG_GPIO__TIO_HV_STATUS, val, - (val & 1) != data->gpio_polarity, - 1 * USEC_PER_MSEC, - 1000 * USEC_PER_MSEC); + ret = vl53l1x_wait_data_ready(data); if (ret) return ret; @@ -460,14 +468,7 @@ static int vl53l1x_read_proximity(struct vl53l1x_data *data, int *val) if (!wait_for_completion_timeout(&data->completion, HZ)) return -ETIMEDOUT; } else { - unsigned int rdy; - - /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ - ret = regmap_read_poll_timeout(data->regmap, - VL53L1X_REG_GPIO__TIO_HV_STATUS, rdy, - (rdy & 1) != data->gpio_polarity, - 1 * USEC_PER_MSEC, - 1000 * USEC_PER_MSEC); + ret = vl53l1x_wait_data_ready(data); if (ret) return ret; } @@ -750,6 +751,6 @@ static struct i2c_driver vl53l1x_driver = { }; module_i2c_driver(vl53l1x_driver); -MODULE_AUTHOR("Siratul Islam <email@sirat.me>"); +MODULE_AUTHOR("Siratul Islam <siratul.islam@linux.dev>"); MODULE_DESCRIPTION("ST VL53L1X ToF ranging sensor driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/iio/resolver/ad2s1200.c b/drivers/iio/resolver/ad2s1200.c index 55bcbbd4021a..27ecc434eee9 100644 --- a/drivers/iio/resolver/ad2s1200.c +++ b/drivers/iio/resolver/ad2s1200.c @@ -143,18 +143,14 @@ static int ad2s1200_probe(struct spi_device *spi) st->sdev = spi; st->sample = devm_gpiod_get(&spi->dev, "adi,sample", GPIOD_OUT_LOW); - if (IS_ERR(st->sample)) { - dev_err(&spi->dev, "Failed to claim SAMPLE gpio: err=%ld\n", - PTR_ERR(st->sample)); - return PTR_ERR(st->sample); - } + if (IS_ERR(st->sample)) + return dev_err_probe(&spi->dev, PTR_ERR(st->sample), + "Failed to claim SAMPLE gpio\n"); st->rdvel = devm_gpiod_get(&spi->dev, "adi,rdvel", GPIOD_OUT_LOW); - if (IS_ERR(st->rdvel)) { - dev_err(&spi->dev, "Failed to claim RDVEL gpio: err=%ld\n", - PTR_ERR(st->rdvel)); - return PTR_ERR(st->rdvel); - } + if (IS_ERR(st->rdvel)) + return dev_err_probe(&spi->dev, PTR_ERR(st->rdvel), + "Failed to claim RDVEL gpio\n"); indio_dev->info = &ad2s1200_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -182,8 +178,8 @@ static const struct of_device_id ad2s1200_of_match[] = { MODULE_DEVICE_TABLE(of, ad2s1200_of_match); static const struct spi_device_id ad2s1200_id[] = { - { "ad2s1200" }, - { "ad2s1205" }, + { .name = "ad2s1200" }, + { .name = "ad2s1205" }, { } }; MODULE_DEVICE_TABLE(spi, ad2s1200_id); diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 1be19fe8aa3f..3b5ec21e3446 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -1276,10 +1276,8 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, return regmap_write(st->regmap, reg, writeval); } -static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +static void ad2s1210_scan_to_buffers(struct iio_dev *indio_dev, s64 timestamp) { - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; struct ad2s1210_state *st = iio_priv(indio_dev); size_t chan = 0; int ret; @@ -1295,15 +1293,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) AD2S1210_REG_POSITION_MSB, &st->sample.raw, 2); if (ret < 0) - goto error_ret; + return; } else { ret = ad2s1210_set_mode(st, MOD_POS); if (ret < 0) - goto error_ret; + return; ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) - goto error_ret; + return; } memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); @@ -1315,15 +1313,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) AD2S1210_REG_VELOCITY_MSB, &st->sample.raw, 2); if (ret < 0) - goto error_ret; + return; } else { ret = ad2s1210_set_mode(st, MOD_VEL); if (ret < 0) - goto error_ret; + return; ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) - goto error_ret; + return; } memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); @@ -1334,16 +1332,22 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) - goto error_ret; + return; st->sample.fault = reg_val; } - ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp); + ad2s1210_push_events(indio_dev, st->sample.fault, timestamp); iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), - pf->timestamp); + timestamp); +} + +static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; -error_ret: + ad2s1210_scan_to_buffers(indio_dev, pf->timestamp); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; @@ -1597,7 +1601,7 @@ static const struct of_device_id ad2s1210_of_match[] = { MODULE_DEVICE_TABLE(of, ad2s1210_of_match); static const struct spi_device_id ad2s1210_id[] = { - { "ad2s1210" }, + { .name = "ad2s1210" }, { } }; MODULE_DEVICE_TABLE(spi, ad2s1210_id); diff --git a/drivers/iio/resolver/ad2s90.c b/drivers/iio/resolver/ad2s90.c index 18f1c905eeac..b7a2dcc42871 100644 --- a/drivers/iio/resolver/ad2s90.c +++ b/drivers/iio/resolver/ad2s90.c @@ -110,7 +110,7 @@ static const struct of_device_id ad2s90_of_match[] = { MODULE_DEVICE_TABLE(of, ad2s90_of_match); static const struct spi_device_id ad2s90_id[] = { - { "ad2s90" }, + { .name = "ad2s90" }, { } }; MODULE_DEVICE_TABLE(spi, ad2s90_id); diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index 58d4c0273932..5e3262e461f4 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -44,7 +44,7 @@ static const struct iio_chan_spec temperature_channels[] = { /* Adjust channel real bits based on report descriptor */ static void temperature_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) + int channel, int size) { channels[channel].scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -100,8 +100,8 @@ static int temperature_read_raw(struct iio_dev *indio_dev, } static int temperature_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct temperature_state *temp_st = iio_priv(indio_dev); @@ -124,7 +124,7 @@ static const struct iio_info temperature_info = { /* Callback handler to send event after all samples are received and captured */ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); @@ -139,8 +139,9 @@ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, size_t raw_len, - char *raw_data, void *pdev) + u32 usage_id, + size_t raw_len, char *raw_data, + void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); @@ -156,10 +157,10 @@ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int temperature_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - unsigned int usage_id, - struct temperature_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct temperature_state *st) { int ret; @@ -170,8 +171,7 @@ static int temperature_parse_report(struct platform_device *pdev, if (ret < 0) return ret; - temperature_adjust_channel_bit_mask(channels, 0, - st->temperature_attr.size); + temperature_adjust_channel_bit_mask(channels, 0, st->temperature_attr.size); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_TEMPERATURE, @@ -212,13 +212,13 @@ static int hid_temperature_probe(struct platform_device *pdev) if (ret) return ret; - temp_chans = devm_kmemdup(&indio_dev->dev, temperature_channels, - sizeof(temperature_channels), GFP_KERNEL); + temp_chans = devm_kmemdup(&pdev->dev, temperature_channels, + sizeof(temperature_channels), GFP_KERNEL); if (!temp_chans) return -ENOMEM; ret = temperature_parse_report(pdev, hsdev, temp_chans, - HID_USAGE_SENSOR_TEMPERATURE, temp_st); + HID_USAGE_SENSOR_TEMPERATURE, temp_st); if (ret) return ret; @@ -231,7 +231,7 @@ static int hid_temperature_probe(struct platform_device *pdev) atomic_set(&temp_st->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &temp_st->common_attributes); + &temp_st->common_attributes); if (ret) return ret; @@ -239,7 +239,7 @@ static int hid_temperature_probe(struct platform_device *pdev) temperature_callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE, - &temperature_callbacks); + &temperature_callbacks); if (ret) goto error_remove_trigger; diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index f8c4917bb118..6efb5252a773 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1994,7 +1994,7 @@ static int ltc2983_probe(struct spi_device *spi) ret = devm_request_irq(dev, spi->irq, ltc2983_irq_handler, IRQF_TRIGGER_RISING, st->info->name, st); if (ret) - return dev_err_probe(dev, ret, "failed to request an irq\n"); + return ret; if (st->info->has_eeprom) { ret = ltc2983_eeprom_cmd(st, LTC2983_EEPROM_WRITE_CMD, @@ -2070,11 +2070,11 @@ static const struct ltc2983_chip_info ltm2985_chip_info_data = { }; static const struct spi_device_id ltc2983_id_table[] = { - { "adt7604", (kernel_ulong_t)&adt7604_chip_info_data }, - { "ltc2983", (kernel_ulong_t)<c2983_chip_info_data }, - { "ltc2984", (kernel_ulong_t)<c2984_chip_info_data }, - { "ltc2986", (kernel_ulong_t)<c2986_chip_info_data }, - { "ltm2985", (kernel_ulong_t)<m2985_chip_info_data }, + { .name = "adt7604", .driver_data = (kernel_ulong_t)&adt7604_chip_info_data }, + { .name = "ltc2983", .driver_data = (kernel_ulong_t)<c2983_chip_info_data }, + { .name = "ltc2984", .driver_data = (kernel_ulong_t)<c2984_chip_info_data }, + { .name = "ltc2986", .driver_data = (kernel_ulong_t)<c2986_chip_info_data }, + { .name = "ltm2985", .driver_data = (kernel_ulong_t)<m2985_chip_info_data }, { } }; MODULE_DEVICE_TABLE(spi, ltc2983_id_table); diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index 13405f3ba829..061f6fe6de5e 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -461,7 +461,7 @@ static int max31856_probe(struct spi_device *spi) } static const struct spi_device_id max31856_id[] = { - { "max31856", 0 }, + { .name = "max31856" }, { } }; MODULE_DEVICE_TABLE(spi, max31856_id); diff --git a/drivers/iio/temperature/max31865.c b/drivers/iio/temperature/max31865.c index 82cf06c4d272..f9c93f95d80b 100644 --- a/drivers/iio/temperature/max31865.c +++ b/drivers/iio/temperature/max31865.c @@ -324,7 +324,7 @@ static int max31865_probe(struct spi_device *spi) } static const struct spi_device_id max31865_id[] = { - { "max31865", 0 }, + { .name = "max31865" }, { } }; MODULE_DEVICE_TABLE(spi, max31865_id); diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index 015afeab1914..5f216e238d55 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -262,15 +262,15 @@ static int maxim_thermocouple_probe(struct spi_device *spi) } static const struct spi_device_id maxim_thermocouple_id[] = { - {"max6675", MAX6675}, - {"max31855", MAX31855}, - {"max31855k", MAX31855K}, - {"max31855j", MAX31855J}, - {"max31855n", MAX31855N}, - {"max31855s", MAX31855S}, - {"max31855t", MAX31855T}, - {"max31855e", MAX31855E}, - {"max31855r", MAX31855R}, + { .name = "max6675", .driver_data = MAX6675 }, + { .name = "max31855", .driver_data = MAX31855 }, + { .name = "max31855k", .driver_data = MAX31855K }, + { .name = "max31855j", .driver_data = MAX31855J }, + { .name = "max31855n", .driver_data = MAX31855N }, + { .name = "max31855s", .driver_data = MAX31855S }, + { .name = "max31855t", .driver_data = MAX31855T }, + { .name = "max31855e", .driver_data = MAX31855E }, + { .name = "max31855r", .driver_data = MAX31855R }, { } }; MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c index 2f6ff87d2a37..4e743424f88e 100644 --- a/drivers/iio/temperature/tmp007.c +++ b/drivers/iio/temperature/tmp007.c @@ -527,10 +527,8 @@ static int tmp007_probe(struct i2c_client *client) NULL, tmp007_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, tmp007_id->name, indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) return ret; - } } return devm_iio_device_register(&client->dev, indio_dev); diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index 6bc18616ad15..74cb8d62bef3 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -9,6 +9,7 @@ * Note: This driver assumes that the sensor has been calibrated beforehand. */ +#include <linux/array_size.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/i2c.h> @@ -38,6 +39,7 @@ #define TMP116_DEVICE_ID 0x1116 #define TMP117_DEVICE_ID 0x0117 +#define TMP119_DEVICE_ID 0x2117 struct tmp117_data { struct i2c_client *client; @@ -139,6 +141,12 @@ static const struct tmp11x_info tmp117_channels_info = { .num_channels = ARRAY_SIZE(tmp117_channels) }; +static const struct tmp11x_info tmp119_channels_info = { + .name = "tmp119", + .channels = tmp117_channels, + .num_channels = ARRAY_SIZE(tmp117_channels) +}; + static const struct iio_info tmp117_info = { .read_raw = tmp117_read_raw, .write_raw = tmp117_write_raw, @@ -172,6 +180,9 @@ static int tmp117_probe(struct i2c_client *client) case TMP117_DEVICE_ID: match_data = &tmp117_channels_info; break; + case TMP119_DEVICE_ID: + match_data = &tmp119_channels_info; + break; default: dev_info(&client->dev, "Unknown device id (0x%x), use fallback compatible\n", @@ -204,6 +215,7 @@ static int tmp117_probe(struct i2c_client *client) static const struct of_device_id tmp117_of_match[] = { { .compatible = "ti,tmp116", .data = &tmp116_channels_info }, { .compatible = "ti,tmp117", .data = &tmp117_channels_info }, + { .compatible = "ti,tmp119", .data = &tmp119_channels_info }, { } }; MODULE_DEVICE_TABLE(of, tmp117_of_match); @@ -211,6 +223,7 @@ MODULE_DEVICE_TABLE(of, tmp117_of_match); static const struct i2c_device_id tmp117_id[] = { { .name = "tmp116", .driver_data = (kernel_ulong_t)&tmp116_channels_info }, { .name = "tmp117", .driver_data = (kernel_ulong_t)&tmp117_channels_info }, + { .name = "tmp119", .driver_data = (kernel_ulong_t)&tmp119_channels_info }, { } }; MODULE_DEVICE_TABLE(i2c, tmp117_id); diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c index 872dd8582003..e5d0a2ac4670 100644 --- a/drivers/iio/test/iio-test-format.c +++ b/drivers/iio/test/iio-test-format.c @@ -200,56 +200,108 @@ static void iio_test_iio_format_value_multiple(struct kunit *test) static void iio_test_iio_format_value_integer_64(struct kunit *test) { int values[2]; - s64 value; char *buf; int ret; buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); - value = 24; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(24, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "24\n"); - value = -24; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(-24, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-24\n"); - value = 0; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(0, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0\n"); - value = UINT_MAX; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(UINT_MAX, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "4294967295\n"); - value = -((s64)UINT_MAX); - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(-((s64)UINT_MAX), &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-4294967295\n"); - value = LLONG_MAX; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(LLONG_MAX, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854775807\n"); - value = LLONG_MIN; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(LLONG_MIN, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854775808\n"); } +static void iio_test_iio_format_value_decimal_64(struct kunit *test) +{ + int values[2]; + char *buf; + int ret; + + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + + /* DECIMAL64_MILLI: positive >= 1, value 1.234 */ + iio_val_s64_decompose(1234, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.234\n"); + + /* DECIMAL64_MICRO: positive >= 1, value 3.141592 */ + iio_val_s64_decompose(3141592, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "3.141592\n"); + + /* DECIMAL64_MILLI: positive < 1, value 0.042 */ + iio_val_s64_decompose(42, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.042\n"); + + /* DECIMAL64_MILLI: negative <= -1, value -1.234 */ + iio_val_s64_decompose(-1234, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.234\n"); + + /* DECIMAL64_MILLI: negative > -1, value -0.123 */ + iio_val_s64_decompose(-123, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.123\n"); + + /* DECIMAL64_MILLI: zero */ + iio_val_s64_decompose(0, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000\n"); + + /* DECIMAL64_NANO: value 1.000000001 */ + iio_val_s64_decompose(1000000001, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000000001\n"); + + /* DECIMAL64_MICRO: large value using upper 32 bits */ + iio_val_s64_decompose(5000000000000042LL, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "5000000000.000042\n"); + + /* limits */ + iio_val_s64_decompose(LLONG_MAX, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372.036854775807\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036.854775807\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854.775807\n"); + + iio_val_s64_decompose(LLONG_MIN, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372.036854775808\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036.854775808\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854.775808\n"); +} + static struct kunit_case iio_format_test_cases[] = { KUNIT_CASE(iio_test_iio_format_value_integer), KUNIT_CASE(iio_test_iio_format_value_fixedpoint), @@ -257,6 +309,7 @@ static struct kunit_case iio_format_test_cases[] = { KUNIT_CASE(iio_test_iio_format_value_fractional_log2), KUNIT_CASE(iio_test_iio_format_value_multiple), KUNIT_CASE(iio_test_iio_format_value_integer_64), + KUNIT_CASE(iio_test_iio_format_value_decimal_64), { } }; diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c index ac6942cf1e44..bcd4a607be19 100644 --- a/drivers/iio/test/iio-test-rescale.c +++ b/drivers/iio/test/iio-test-rescale.c @@ -575,7 +575,7 @@ static const struct rescale_tc_data offset_cases[] = { static void case_to_desc(const struct rescale_tc_data *t, char *desc) { - strcpy(desc, t->name); + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); } KUNIT_ARRAY_PARAM(iio_rescale_scale, scale_cases, case_to_desc); diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c index ff44f9978b71..4fa42f88bcfa 100644 --- a/drivers/input/joystick/adc-joystick.c +++ b/drivers/input/joystick/adc-joystick.c @@ -329,3 +329,4 @@ module_platform_driver(adc_joystick_driver); MODULE_DESCRIPTION("Input driver for joysticks connected over ADC"); MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c index f1753207429d..d687459a0c80 100644 --- a/drivers/input/keyboard/adc-keys.c +++ b/drivers/input/keyboard/adc-keys.c @@ -202,3 +202,4 @@ module_platform_driver(adc_keys_driver); MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c index 98d5b2ba63fb..89c4d7b2b89e 100644 --- a/drivers/input/touchscreen/colibri-vf50-ts.c +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -372,3 +372,4 @@ module_platform_driver(vf50_touch_driver); MODULE_AUTHOR("Sanchayan Maity"); MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/touchscreen/resistive-adc-touch.c b/drivers/input/touchscreen/resistive-adc-touch.c index 68f7e2e28f37..c89edc1280fe 100644 --- a/drivers/input/touchscreen/resistive-adc-touch.c +++ b/drivers/input/touchscreen/resistive-adc-touch.c @@ -300,3 +300,4 @@ module_platform_driver(grts_driver); MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>"); MODULE_DESCRIPTION("Generic ADC Resistive Touch Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 73a9db2c7537..4aa991a54101 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -548,7 +548,7 @@ struct icc_path *of_icc_get_by_index(struct device *dev, int idx) path->name = kasprintf(GFP_KERNEL, "%s-%s", src_data->node->name, dst_data->node->name); if (!path->name) { - kfree(path); + icc_put(path); path = ERR_PTR(-ENOMEM); } @@ -646,8 +646,9 @@ struct icc_path *icc_get(struct device *dev, const char *src, const char *dst) path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name); if (!path->name) { - kfree(path); - path = ERR_PTR(-ENOMEM); + mutex_unlock(&icc_lock); + icc_put(path); + return ERR_PTR(-ENOMEM); } out: mutex_unlock(&icc_lock); diff --git a/drivers/interconnect/debugfs-client.c b/drivers/interconnect/debugfs-client.c index 08df9188ef94..91f86d9237a6 100644 --- a/drivers/interconnect/debugfs-client.c +++ b/drivers/interconnect/debugfs-client.c @@ -142,6 +142,8 @@ int icc_debugfs_client_init(struct dentry *icc_dir) int ret; pdev = platform_device_alloc("icc-debugfs-client", PLATFORM_DEVID_NONE); + if (!pdev) + return -ENOMEM; ret = platform_device_add(pdev); if (ret) { diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 5b8a094ca4ed..b3187acf90b8 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config INTERCONNECT_QCOM tristate "Qualcomm Network-on-Chip interconnect drivers" - depends on ARCH_QCOM + depends on ARCH_QCOM || COMPILE_TEST help Support for Qualcomm's Network-on-Chip interconnect hardware. @@ -47,6 +47,16 @@ config INTERCONNECT_QCOM_KAANAPALI This is a driver for the Qualcomm Network-on-Chip on kaanapali-based platforms. +config INTERCONNECT_QCOM_MAILI + tristate "Qualcomm MAILI interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + default ARCH_QCOM + help + This is a driver for the Qualcomm Network-on-Chip on maili-based + platforms. + config INTERCONNECT_QCOM_MSM8909 tristate "Qualcomm MSM8909 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 1c7d410b40cc..5f139a370bbe 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -8,6 +8,7 @@ qnoc-eliza-objs := eliza.o qnoc-glymur-objs := glymur.o qnoc-hawi-objs := hawi.o qnoc-kaanapali-objs := kaanapali.o +qnoc-maili-objs := maili.o qnoc-milos-objs := milos.o qnoc-msm8909-objs := msm8909.o qnoc-msm8916-objs := msm8916.o @@ -56,6 +57,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_ELIZA) += qnoc-eliza.o obj-$(CONFIG_INTERCONNECT_QCOM_GLYMUR) += qnoc-glymur.o obj-$(CONFIG_INTERCONNECT_QCOM_HAWI) += qnoc-hawi.o obj-$(CONFIG_INTERCONNECT_QCOM_KAANAPALI) += qnoc-kaanapali.o +obj-$(CONFIG_INTERCONNECT_QCOM_MAILI) += qnoc-maili.o obj-$(CONFIG_INTERCONNECT_QCOM_MILOS) += qnoc-milos.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8909) += qnoc-msm8909.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 23a1d116e79a..ca4c8a944755 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -479,13 +479,11 @@ int qnoc_probe(struct platform_device *pdev) cd_num = 0; } - qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + qp = devm_kzalloc(dev, struct_size(qp, intf_clks, cd_num), GFP_KERNEL); if (!qp) return -ENOMEM; - qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL); - if (!qp->intf_clks) - return -ENOMEM; + qp->num_intf_clks = cd_num; if (desc->bus_clk_desc) { qp->bus_clk_desc = devm_kzalloc(dev, sizeof(*qp->bus_clk_desc), @@ -507,7 +505,6 @@ int qnoc_probe(struct platform_device *pdev) return -ENOMEM; data->num_nodes = num_nodes; - qp->num_intf_clks = cd_num; for (i = 0; i < cd_num; i++) qp->intf_clks[i].id = cds[i]; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 7d1cb2efa9ee..cdd5f444b152 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -40,7 +40,6 @@ struct rpm_clk_resource { /** * struct qcom_icc_provider - Qualcomm specific interconnect provider * @provider: generic interconnect provider - * @num_intf_clks: the total number of intf_clks clk_bulk_data entries * @type: the ICC provider type * @regmap: regmap for QoS registers read/write access * @qos_offset: offset to QoS registers @@ -49,13 +48,13 @@ struct rpm_clk_resource { * @bus_clk_rate: bus clock rate in Hz * @bus_clk_desc: a pointer to a rpm_clk_resource description of bus clocks * @bus_clk: a pointer to a HLOS-owned bus clock - * @intf_clks: a clk_bulk_data array of interface clocks * @keep_alive: whether to always keep a minimum vote on the bus clocks * @ignore_enxio: whether to ignore ENXIO errors (for MSM8974) + * @num_intf_clks: the total number of intf_clks clk_bulk_data entries + * @intf_clks: a clk_bulk_data array of interface clocks */ struct qcom_icc_provider { struct icc_provider provider; - int num_intf_clks; enum qcom_icc_type type; struct regmap *regmap; unsigned int qos_offset; @@ -64,9 +63,10 @@ struct qcom_icc_provider { u32 bus_clk_rate[QCOM_SMD_RPM_STATE_NUM]; const struct rpm_clk_resource *bus_clk_desc; struct clk *bus_clk; - struct clk_bulk_data *intf_clks; bool keep_alive; bool ignore_enxio; + int num_intf_clks; + struct clk_bulk_data intf_clks[] __counted_by(num_intf_clks); }; /** diff --git a/drivers/interconnect/qcom/maili.c b/drivers/interconnect/qcom/maili.c new file mode 100644 index 000000000000..e5eed7f998f9 --- /dev/null +++ b/drivers/interconnect/qcom/maili.c @@ -0,0 +1,2091 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + */ + +#include <linux/device.h> +#include <linux/interconnect.h> +#include <linux/interconnect-provider.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <dt-bindings/interconnect/qcom,maili-rpmh.h> + +#include "bcm-voter.h" +#include "icc-rpmh.h" + +static struct qcom_icc_node ddr_eff_veto_slave = { + .name = "ddr_eff_veto_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup3_core_slave = { + .name = "qup3_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup4_core_slave = { + .name = "qup4_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup5_core_slave = { + .name = "qup5_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipc_router_fence = { + .name = "qhs_ipc_router_fence", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_soccp = { + .name = "qhs_soccp", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie = { + .name = "xs_pcie", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie_g4x1 = { + .name = "xs_pcie_g4x1", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_lpi_cc = { + .name = "qhs_lpi_cc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qns_lb = { + .name = "qns_lb", + .channels = 4, + .buswidth = 16, +}; + +static struct qcom_icc_node srvc_llclpi_noc = { + .name = "srvc_llclpi_noc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_llclpi_noc_chipcx = { + .name = "srvc_llclpi_noc_chipcx", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node ddr_rt_slave = { + .name = "ddr_rt_slave", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_pcie_aggre_noc = { + .name = "srvc_pcie_aggre_noc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_boot_rom = { + .name = "qhs_boot_rom", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_crypto_cfg = { + .name = "qhs_crypto_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_eva_cfg = { + .name = "qhs_eva_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_i2c = { + .name = "qhs_i2c", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_iris_cfg = { + .name = "qhs_iris_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_cfg = { + .name = "qhs_pcie_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_g4x1_cfg = { + .name = "qhs_pcie_g4x1_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup3 = { + .name = "qhs_qup3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup4 = { + .name = "qhs_qup4", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup5 = { + .name = "qhs_qup5", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_usb3 = { + .name = "qhs_usb3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_qdss_cfg = { + .name = "qss_qdss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_qdss_stm = { + .name = "qss_qdss_stm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_tcsr = { + .name = "qss_tcsr", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node ddr_eff_veto_master = { + .name = "ddr_eff_veto_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ddr_eff_veto_slave }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_node qup3_core_master = { + .name = "qup3_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_node qup4_core_master = { + .name = "qup4_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup4_core_slave }, +}; + +static struct qcom_icc_node qup5_core_master = { + .name = "qup5_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup5_core_slave }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 2, + .link_nodes = { &xs_pcie, &xs_pcie_g4x1 }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc_llclpi_noc = { + .name = "qnm_lpiaon_noc_llclpi_noc", + .channels = 1, + .buswidth = 16, + .num_links = 4, + .link_nodes = { &qhs_lpi_cc, &qns_lb, + &srvc_llclpi_noc, &srvc_llclpi_noc_chipcx }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .channels = 4, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ebi }, +}; + +static struct qcom_icc_node ddr_rt_mc = { + .name = "ddr_rt_mc", + .channels = 4, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ddr_rt_slave }, +}; + +static struct qcom_icc_node qsm_pcie_anoc_cfg = { + .name = "qsm_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &srvc_pcie_aggre_noc }, +}; + +static struct qcom_icc_node qsm_cfg_center = { + .name = "qsm_cfg_center", + .channels = 1, + .buswidth = 4, + .num_links = 14, + .link_nodes = { &qhs_boot_rom, &qhs_clk_ctl, + &qhs_crypto_cfg, &qhs_gpuss_cfg, + &qhs_imem_cfg, &qhs_ipc_router, + &qhs_mss_cfg, &qhs_prng, + &qhs_tlmm, &qhs_vsense_ctrl_cfg, + &qss_qdss_cfg, &qss_qdss_stm, + &qss_tcsr, &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_east = { + .name = "qsm_cfg_east", + .channels = 1, + .buswidth = 4, + .num_links = 2, + .link_nodes = { &qhs_qup2, &qhs_qup4 }, +}; + +static struct qcom_icc_node qsm_cfg_mm_hf = { + .name = "qsm_cfg_mm_hf", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qhs_display_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_mm_sf = { + .name = "qsm_cfg_mm_sf", + .channels = 1, + .buswidth = 4, + .num_links = 3, + .link_nodes = { &qhs_camera_cfg, &qhs_eva_cfg, + &qhs_iris_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_north = { + .name = "qsm_cfg_north", + .channels = 1, + .buswidth = 4, + .num_links = 3, + .link_nodes = { &qhs_ahb2phy0, &qhs_ufs_mem_cfg, + &qhs_usb3 }, +}; + +static struct qcom_icc_node qsm_cfg_west = { + .name = "qsm_cfg_west", + .channels = 1, + .buswidth = 4, + .num_links = 2, + .link_nodes = { &qhs_qup1, &qhs_sdc2 }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .channels = 4, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &llcc_mc }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_pcie }, +}; + +static struct qcom_icc_node qns_llc_lpinoc = { + .name = "qns_llc_lpinoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpiaon_noc_llclpi_noc }, +}; + +static struct qcom_icc_node qss_pcie_anoc_cfg = { + .name = "qss_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_pcie_anoc_cfg }, +}; + +static struct qcom_icc_node qss_stdst_center_cfg = { + .name = "qss_stdst_center_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_center }, +}; + +static struct qcom_icc_node qss_stdst_east_cfg = { + .name = "qss_stdst_east_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_east }, +}; + +static struct qcom_icc_node qss_stdst_mm_hf_cfg = { + .name = "qss_stdst_mm_hf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_mm_hf }, +}; + +static struct qcom_icc_node qss_stdst_mm_sf_cfg = { + .name = "qss_stdst_mm_sf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_mm_sf }, +}; + +static struct qcom_icc_node qss_stdst_north_cfg = { + .name = "qss_stdst_north_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_north }, +}; + +static struct qcom_icc_node qss_stdst_west_cfg = { + .name = "qss_stdst_west_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_west }, +}; + +static struct qcom_icc_node alm_gic = { + .name = "alm_gic", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14d000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qnm_qpace = { + .name = "qnm_qpace", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x153000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qsm_cfg_south = { + .name = "qsm_cfg_south", + .channels = 1, + .buswidth = 4, + .num_links = 8, + .link_nodes = { &qhs_i2c, &qhs_pcie_cfg, + &qhs_pcie_g4x1_cfg, &qhs_qspi, + &qhs_qup3, &qhs_qup5, + &qhs_sdc4, &qss_pcie_anoc_cfg }, +}; + +static struct qcom_icc_node qss_stdst_south_cfg = { + .name = "qss_stdst_south_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_south }, +}; + +static struct qcom_icc_node qsm_cnoc_main = { + .name = "qsm_cnoc_main", + .channels = 1, + .buswidth = 4, + .num_links = 7, + .link_nodes = { &qss_stdst_center_cfg, &qss_stdst_east_cfg, + &qss_stdst_mm_hf_cfg, &qss_stdst_mm_sf_cfg, + &qss_stdst_north_cfg, &qss_stdst_south_cfg, + &qss_stdst_west_cfg }, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cnoc_main }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 8, + .link_nodes = { &qhs_aoss, &qhs_ipa, + &qhs_ipc_router_fence, &qhs_soccp, + &qhs_tme_cfg, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem }, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_cnoc }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x145000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x147000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .channels = 2, + .buswidth = 32, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x51000, 0xd1000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_lpass_gemnoc = { + .name = "qnm_lpass_gemnoc", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x149000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mdsp = { + .name = "qnm_mdsp", + .channels = 1, + .buswidth = 16, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x55000, 0xd5000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x57000, 0xd7000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x59000, 0xd9000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14b000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14f000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_wlan_q6 = { + .name = "qnm_wlan_q6", + .channels = 1, + .buswidth = 8, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = { + .name = "qns_lpass_ag_noc_gemnoc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpass_gemnoc }, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_hf }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_sf }, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_nsp_gemnoc }, +}; + +static struct qcom_icc_node qns_pcie_gemnoc = { + .name = "qns_pcie_gemnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_pcie }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_snoc_sf }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc = { + .name = "qnm_lpiaon_noc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_lpass_ag_noc_gemnoc }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x50000, 0x51000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x52000, 0x53000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_camnoc_nrt_icp_sf = { + .name = "qnm_camnoc_nrt_icp_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x20000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_rt_cdm_sf = { + .name = "qnm_camnoc_rt_cdm_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x28000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x21000, 0x22000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_mdss_dcp = { + .name = "qnm_mdss_dcp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x29000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_vapss_hcp = { + .name = "qnm_vapss_hcp", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x24000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_eva = { + .name = "qnm_video_eva", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x25000, 0x26000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_mvp = { + .name = "qnm_video_mvp", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x23000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x27000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_nsp = { + .name = "qnm_nsp", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_node xm_pcie = { + .name = "xm_pcie", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_node xm_pcie_g4x1 = { + .name = "xm_pcie_g4x1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_node qnm_aggre_noc = { + .name = "qnm_aggre_noc", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x20000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_apss_noc = { + .name = "qnm_apss_noc", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1e000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_cnoc_data = { + .name = "qnm_cnoc_data", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1f000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_aggre_noc }, +}; + +static struct qcom_icc_node qns_lpass_aggnoc = { + .name = "qns_lpass_aggnoc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpiaon_noc }, +}; + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x49000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x48000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup3 = { + .name = "qhm_qup3", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x46000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup4 = { + .name = "qhm_qup4", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x47000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup5 = { + .name = "qhm_qup5", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4e000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x40000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x41000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_qup1 = { + .name = "qxm_qup1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4d000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_soccp = { + .name = "qxm_soccp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x45000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x42000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x43000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x44000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4b000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_usb3 = { + .name = "xm_usb3", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4c000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qnm_lpass_lpinoc = { + .name = "qnm_lpass_lpinoc", + .channels = 1, + .buswidth = 32, + .num_links = 2, + .link_nodes = { &qns_llc_lpinoc, &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_node qns_lpi_aon_noc = { + .name = "qns_lpi_aon_noc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpass_lpinoc }, +}; + +static struct qcom_icc_node qnm_lpinoc_dsp_qns4m = { + .name = "qnm_lpinoc_dsp_qns4m", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_lpi_aon_noc }, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .enable_mask = BIT(3), + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .enable_mask = BIT(0), + .keepalive = true, + .num_nodes = 25, + .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie, + &qhs_aoss, &qhs_ipa, + &qhs_ipc_router_fence, &qhs_soccp, + &qhs_tme_cfg, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem, + &xs_pcie, &xs_pcie_g4x1, + &qsm_cfg_center, &qsm_cfg_east, + &qsm_cfg_mm_hf, &qsm_cfg_mm_sf, + &qsm_cfg_north, &qsm_cfg_south, + &qsm_cfg_west, &qhs_ahb2phy0, + &qhs_boot_rom, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_crypto_cfg, + &qhs_eva_cfg }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 6, + .nodes = { &qhs_display_cfg, &qhs_qup1, + &qhs_qup2, &qhs_qup3, + &qhs_qup4, &qhs_qup5 }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qnm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_de0 = { + .name = "DE0", + .enable_mask = BIT(0), + .num_nodes = 1, + .nodes = { &ddr_eff_veto_slave }, +}; + +static struct qcom_icc_bcm bcm_lp0 = { + .name = "LP0", + .num_nodes = 5, + .nodes = { &qnm_lpiaon_noc_llclpi_noc, &qns_lb, + &qnm_lpass_lpinoc, &qns_llc_lpinoc, + &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mc5 = { + .name = "MC5", + .num_nodes = 1, + .nodes = { &ddr_rt_slave }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .enable_mask = BIT(0), + .num_nodes = 9, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_nrt_icp_sf, + &qnm_camnoc_rt_cdm_sf, &qnm_camnoc_sf, + &qnm_vapss_hcp, &qnm_video_cv_cpu, + &qnm_video_mvp, &qnm_video_v_cpu, + &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qpc0 = { + .name = "QPC0", + .num_nodes = 1, + .nodes = { &qnm_qpace }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup2 = { + .name = "QUP2", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup3 = { + .name = "QUP3", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup4 = { + .name = "QUP4", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup4_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup5 = { + .name = "QUP5", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup5_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .enable_mask = BIT(0), + .num_nodes = 15, + .nodes = { &alm_gic, &alm_gpu_tcu, + &alm_sys_tcu, &chm_apps, + &qnm_gpu, &qnm_lpass_gemnoc, + &qnm_mdsp, &qnm_mnoc_hf, + &qnm_mnoc_sf, &qnm_nsp_gemnoc, + &qnm_pcie, &qnm_snoc_sf, + &qnm_wlan_q6, &qns_gem_noc_cnoc, + &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qnm_aggre_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_bcm * const aggre1_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_QUP_3] = &qhm_qup3, + [MASTER_QUP_4] = &qhm_qup4, + [MASTER_QUP_5] = &qhm_qup5, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_QUP_1] = &qxm_qup1, + [MASTER_SOCCP_PROC] = &qxm_soccp, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_2] = &xm_sdc2, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB3] = &xm_usb3, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct regmap_config maili_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x56200, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_aggre1_noc = { + .config = &maili_aggre1_noc_regmap_config, + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .bcms = aggre1_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_de0, + &bcm_qup0, + &bcm_qup1, + &bcm_qup2, + &bcm_qup3, + &bcm_qup4, + &bcm_qup5, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_DDR_EFF_VETO] = &ddr_eff_veto_master, + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [MASTER_QUP_CORE_3] = &qup3_core_master, + [MASTER_QUP_CORE_4] = &qup4_core_master, + [MASTER_QUP_CORE_5] = &qup5_core_master, + [SLAVE_DDR_EFF_VETO] = &ddr_eff_veto_slave, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_2] = &qup2_core_slave, + [SLAVE_QUP_CORE_3] = &qup3_core_slave, + [SLAVE_QUP_CORE_4] = &qup4_core_slave, + [SLAVE_QUP_CORE_5] = &qup5_core_slave, +}; + +static const struct qcom_icc_desc maili_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_main_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node * const cnoc_main_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_FENCE] = &qhs_ipc_router_fence, + [SLAVE_SOCCP] = &qhs_soccp, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PCIE_0] = &xs_pcie, + [SLAVE_PCIE_1] = &xs_pcie_g4x1, +}; + +static const struct regmap_config maili_cnoc_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20000, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_cnoc_main = { + .config = &maili_cnoc_main_regmap_config, + .nodes = cnoc_main_nodes, + .num_nodes = ARRAY_SIZE(cnoc_main_nodes), + .bcms = cnoc_main_bcms, + .num_bcms = ARRAY_SIZE(cnoc_main_bcms), +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_qpc0, + &bcm_sh0, + &bcm_sh1, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GIC] = &alm_gic, + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_LPASS_GEM_NOC] = &qnm_lpass_gemnoc, + [MASTER_MSS_PROC] = &qnm_mdsp, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_QPACE] = &qnm_qpace, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_WLAN_Q6] = &qnm_wlan_q6, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct regmap_config maili_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x160200, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_gem_noc = { + .config = &maili_gem_noc_regmap_config, + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_bcm * const llclpi_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const llclpi_noc_nodes[] = { + [MASTER_LPIAON_NOC_LLCLPI_NOC] = &qnm_lpiaon_noc_llclpi_noc, + [SLAVE_LPASS_LPI_CC] = &qhs_lpi_cc, + [SLAVE_LLCC_ISLAND] = &qns_lb, + [SLAVE_SERVICE_LLCLPI_NOC] = &srvc_llclpi_noc, + [SLAVE_SERVICE_LLCLPI_NOC_CHIPCX] = &srvc_llclpi_noc_chipcx, +}; + +static const struct regmap_config maili_llclpi_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17200, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_llclpi_noc = { + .config = &maili_llclpi_noc_regmap_config, + .nodes = llclpi_noc_nodes, + .num_nodes = ARRAY_SIZE(llclpi_noc_nodes), + .bcms = llclpi_noc_bcms, + .num_bcms = ARRAY_SIZE(llclpi_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { + [MASTER_LPIAON_NOC] = &qnm_lpiaon_noc, + [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, +}; + +static const struct regmap_config maili_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xc080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_lpass_ag_noc = { + .config = &maili_lpass_ag_noc_regmap_config, + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), +}; + +static struct qcom_icc_bcm * const lpass_lpiaon_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = { + [MASTER_LPASS_LPINOC] = &qnm_lpass_lpinoc, + [SLAVE_LPIAON_NOC_LLCLPI_NOC] = &qns_llc_lpinoc, + [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, +}; + +static const struct regmap_config maili_lpass_lpiaon_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x19080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_lpass_lpiaon_noc = { + .config = &maili_lpass_lpiaon_noc_regmap_config, + .nodes = lpass_lpiaon_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes), + .bcms = lpass_lpiaon_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_lpiaon_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { + [MASTER_LPASS_PROC] = &qnm_lpinoc_dsp_qns4m, + [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, +}; + +static const struct regmap_config maili_lpass_lpicx_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x46080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_lpass_lpicx_noc = { + .config = &maili_lpass_lpicx_noc_regmap_config, + .nodes = lpass_lpicx_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, + &bcm_mc5, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [MASTER_DDR_RT] = &ddr_rt_mc, + [SLAVE_EBI1] = &ebi, + [SLAVE_DDR_RT] = &ddr_rt_slave, +}; + +static const struct qcom_icc_desc maili_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_NRT_ICP_SF] = &qnm_camnoc_nrt_icp_sf, + [MASTER_CAMNOC_RT_CDM_SF] = &qnm_camnoc_rt_cdm_sf, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_MDP] = &qnm_mdp, + [MASTER_MDSS_DCP] = &qnm_mdss_dcp, + [MASTER_CDSP_HCP] = &qnm_vapss_hcp, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [MASTER_VIDEO_EVA] = &qnm_video_eva, + [MASTER_VIDEO_MVP] = &qnm_video_mvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, +}; + +static const struct regmap_config maili_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5f800, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_mmss_noc = { + .config = &maili_mmss_noc_regmap_config, + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const nsp_noc_bcms[] = { + &bcm_co0, +}; + +static struct qcom_icc_node * const nsp_noc_nodes[] = { + [MASTER_CDSP_PROC] = &qnm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, +}; + +static const struct regmap_config maili_nsp_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x21280, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_nsp_noc = { + .config = &maili_nsp_noc_regmap_config, + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), +}; + +static struct qcom_icc_bcm * const pcie_anoc_bcms[] = { + &bcm_sn3, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg, + [MASTER_PCIE_0] = &xm_pcie, + [MASTER_PCIE_1] = &xm_pcie_g4x1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_gemnoc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static const struct regmap_config maili_pcie_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x12400, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_pcie_anoc = { + .config = &maili_pcie_anoc_regmap_config, + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const stdst_cfg_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const stdst_cfg_nodes[] = { + [MASTER_CFG_CENTER] = &qsm_cfg_center, + [MASTER_CFG_EAST] = &qsm_cfg_east, + [MASTER_CFG_MM_HF] = &qsm_cfg_mm_hf, + [MASTER_CFG_MM_SF] = &qsm_cfg_mm_sf, + [MASTER_CFG_NORTH] = &qsm_cfg_north, + [MASTER_CFG_SOUTH] = &qsm_cfg_south, + [MASTER_CFG_WEST] = &qsm_cfg_west, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_BOOT_ROM] = &qhs_boot_rom, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CRYPTO_CFG] = &qhs_crypto_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_EVA_CFG] = &qhs_eva_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_I2C] = &qhs_i2c, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_IRIS_CFG] = &qhs_iris_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_PCIE_0_CFG] = &qhs_pcie_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie_g4x1_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_QUP_3] = &qhs_qup3, + [SLAVE_QUP_4] = &qhs_qup4, + [SLAVE_QUP_5] = &qhs_qup5, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SDCC_4] = &qhs_sdc4, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB3] = &qhs_usb3, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg, + [SLAVE_QDSS_CFG] = &qss_qdss_cfg, + [SLAVE_QDSS_STM] = &qss_qdss_stm, + [SLAVE_TCSR] = &qss_tcsr, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct regmap_config maili_stdst_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9000, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_stdst_cfg = { + .config = &maili_stdst_cfg_regmap_config, + .nodes = stdst_cfg_nodes, + .num_nodes = ARRAY_SIZE(stdst_cfg_nodes), + .bcms = stdst_cfg_bcms, + .num_bcms = ARRAY_SIZE(stdst_cfg_bcms), +}; + +static struct qcom_icc_node * const stdst_main_nodes[] = { + [MASTER_CNOC_STARDUST] = &qsm_cnoc_main, + [SLAVE_STARDUST_CENTER_CFG] = &qss_stdst_center_cfg, + [SLAVE_STARDUST_EAST_CFG] = &qss_stdst_east_cfg, + [SLAVE_STARDUST_MM_HF_CFG] = &qss_stdst_mm_hf_cfg, + [SLAVE_STARDUST_MM_SF_CFG] = &qss_stdst_mm_sf_cfg, + [SLAVE_STARDUST_NORTH_CFG] = &qss_stdst_north_cfg, + [SLAVE_STARDUST_SOUTH_CFG] = &qss_stdst_south_cfg, + [SLAVE_STARDUST_WEST_CFG] = &qss_stdst_west_cfg, +}; + +static const struct regmap_config maili_stdst_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf9000, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_stdst_main = { + .config = &maili_stdst_main_regmap_config, + .nodes = stdst_main_nodes, + .num_nodes = ARRAY_SIZE(stdst_main_nodes), +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn2, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre_noc, + [MASTER_APSS_NOC] = &qnm_apss_noc, + [MASTER_CNOC_SNOC] = &qnm_cnoc_data, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, +}; + +static const struct regmap_config maili_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_system_noc = { + .config = &maili_system_noc_regmap_config, + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,maili-aggre-noc", .data = &maili_aggre1_noc }, + { .compatible = "qcom,maili-clk-virt", .data = &maili_clk_virt }, + { .compatible = "qcom,maili-cnoc-main", .data = &maili_cnoc_main }, + { .compatible = "qcom,maili-gem-noc", .data = &maili_gem_noc }, + { .compatible = "qcom,maili-llclpi-noc", .data = &maili_llclpi_noc }, + { .compatible = "qcom,maili-lpass-ag-noc", .data = &maili_lpass_ag_noc }, + { .compatible = "qcom,maili-lpass-lpiaon-noc", .data = &maili_lpass_lpiaon_noc }, + { .compatible = "qcom,maili-lpass-lpicx-noc", .data = &maili_lpass_lpicx_noc }, + { .compatible = "qcom,maili-mc-virt", .data = &maili_mc_virt }, + { .compatible = "qcom,maili-mmss-noc", .data = &maili_mmss_noc }, + { .compatible = "qcom,maili-nsp-noc", .data = &maili_nsp_noc }, + { .compatible = "qcom,maili-pcie-anoc", .data = &maili_pcie_anoc }, + { .compatible = "qcom,maili-stdst-cfg", .data = &maili_stdst_cfg }, + { .compatible = "qcom,maili-stdst-main", .data = &maili_stdst_main }, + { .compatible = "qcom,maili-system-noc", .data = &maili_system_noc }, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-maili", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("Qualcomm Maili NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c index b33f00da1880..ecad636b53e0 100644 --- a/drivers/interconnect/qcom/osm-l3.c +++ b/drivers/interconnect/qcom/osm-l3.c @@ -60,6 +60,7 @@ struct qcom_osm_l3_desc { unsigned int lut_row_size; unsigned int reg_freq_lut; unsigned int reg_perf_state; + unsigned int lut_max_entries; }; #define DEFINE_QNODE(_name, _buswidth) \ @@ -90,6 +91,7 @@ static const struct qcom_osm_l3_desc osm_l3 = { .lut_row_size = OSM_LUT_ROW_SIZE, .reg_freq_lut = OSM_REG_FREQ_LUT, .reg_perf_state = OSM_REG_PERF_STATE, + .lut_max_entries = LUT_MAX_ENTRIES, }; static const struct qcom_osm_l3_desc epss_l3_perf_state = { @@ -98,6 +100,16 @@ static const struct qcom_osm_l3_desc epss_l3_perf_state = { .lut_row_size = EPSS_LUT_ROW_SIZE, .reg_freq_lut = EPSS_REG_FREQ_LUT, .reg_perf_state = EPSS_REG_PERF_STATE, + .lut_max_entries = LUT_MAX_ENTRIES, +}; + +static const struct qcom_osm_l3_desc shikra_epss_l3_perf_state = { + .nodes = epss_l3_nodes, + .num_nodes = ARRAY_SIZE(epss_l3_nodes), + .lut_row_size = EPSS_LUT_ROW_SIZE, + .reg_freq_lut = EPSS_REG_FREQ_LUT, + .reg_perf_state = EPSS_REG_PERF_STATE, + .lut_max_entries = 12, }; static const struct qcom_osm_l3_desc epss_l3_l3_vote = { @@ -106,6 +118,7 @@ static const struct qcom_osm_l3_desc epss_l3_l3_vote = { .lut_row_size = EPSS_LUT_ROW_SIZE, .reg_freq_lut = EPSS_REG_FREQ_LUT, .reg_perf_state = EPSS_REG_L3_VOTE, + .lut_max_entries = LUT_MAX_ENTRIES, }; static int qcom_osm_l3_set(struct icc_node *src, struct icc_node *dst) @@ -189,7 +202,7 @@ static int qcom_osm_l3_probe(struct platform_device *pdev) qp->reg_perf_state = desc->reg_perf_state; - for (i = 0; i < LUT_MAX_ENTRIES; i++) { + for (i = 0; i < desc->lut_max_entries; i++) { info = readl_relaxed(qp->base + desc->reg_freq_lut + i * desc->lut_row_size); src = FIELD_GET(LUT_SRC, info); @@ -272,6 +285,7 @@ static const struct of_device_id osm_l3_of_match[] = { { .compatible = "qcom,sc7180-osm-l3", .data = &osm_l3 }, { .compatible = "qcom,sc7280-epss-l3", .data = &epss_l3_perf_state }, { .compatible = "qcom,sdm845-osm-l3", .data = &osm_l3 }, + { .compatible = "qcom,shikra-epss-l3", .data = &shikra_epss_l3_perf_state }, { .compatible = "qcom,sm8150-osm-l3", .data = &osm_l3 }, { .compatible = "qcom,sc8180x-osm-l3", .data = &osm_l3 }, { .compatible = "qcom,sm8250-epss-l3", .data = &epss_l3_perf_state }, diff --git a/drivers/interconnect/qcom/sc8280xp.c b/drivers/interconnect/qcom/sc8280xp.c index 4110536664d0..15e284fcd89a 100644 --- a/drivers/interconnect/qcom/sc8280xp.c +++ b/drivers/interconnect/qcom/sc8280xp.c @@ -215,6 +215,11 @@ static struct qcom_icc_node qhm_qspi = { .name = "qhm_qspi", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x5000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -223,6 +228,11 @@ static struct qcom_icc_node qhm_qup1 = { .name = "qhm_qup1", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x6000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -231,6 +241,11 @@ static struct qcom_icc_node qhm_qup2 = { .name = "qhm_qup2", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -247,6 +262,12 @@ static struct qcom_icc_node qxm_ipa = { .name = "qxm_ipa", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x8000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -255,6 +276,11 @@ static struct qcom_icc_node xm_emac_1 = { .name = "xm_emac_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -263,6 +289,11 @@ static struct qcom_icc_node xm_sdc4 = { .name = "xm_sdc4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -271,6 +302,11 @@ static struct qcom_icc_node xm_ufs_mem = { .name = "xm_ufs_mem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -279,6 +315,11 @@ static struct qcom_icc_node xm_usb3_0 = { .name = "xm_usb3_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -287,6 +328,11 @@ static struct qcom_icc_node xm_usb3_1 = { .name = "xm_usb3_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -295,6 +341,11 @@ static struct qcom_icc_node xm_usb3_mp = { .name = "xm_usb3_mp", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -303,6 +354,11 @@ static struct qcom_icc_node xm_usb4_host0 = { .name = "xm_usb4_host0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -311,6 +367,11 @@ static struct qcom_icc_node xm_usb4_host1 = { .name = "xm_usb4_host1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -319,6 +380,11 @@ static struct qcom_icc_node qhm_qdss_bam = { .name = "qhm_qdss_bam", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -327,6 +393,11 @@ static struct qcom_icc_node qhm_qup0 = { .name = "qhm_qup0", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -343,6 +414,12 @@ static struct qcom_icc_node qxm_crypto = { .name = "qxm_crypto", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -367,6 +444,11 @@ static struct qcom_icc_node xm_emac_0 = { .name = "xm_emac_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -375,6 +457,11 @@ static struct qcom_icc_node xm_pcie3_0 = { .name = "xm_pcie3_0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -383,6 +470,11 @@ static struct qcom_icc_node xm_pcie3_1 = { .name = "xm_pcie3_1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -391,6 +483,11 @@ static struct qcom_icc_node xm_pcie3_2a = { .name = "xm_pcie3_2a", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -399,6 +496,11 @@ static struct qcom_icc_node xm_pcie3_2b = { .name = "xm_pcie3_2b", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -407,6 +509,11 @@ static struct qcom_icc_node xm_pcie3_3a = { .name = "xm_pcie3_3a", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -415,6 +522,11 @@ static struct qcom_icc_node xm_pcie3_3b = { .name = "xm_pcie3_3b", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -423,6 +535,11 @@ static struct qcom_icc_node xm_pcie3_4 = { .name = "xm_pcie3_4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -431,6 +548,11 @@ static struct qcom_icc_node xm_qdss_etr = { .name = "xm_qdss_etr", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -439,6 +561,11 @@ static struct qcom_icc_node xm_sdc2 = { .name = "xm_sdc2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x8000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -447,6 +574,11 @@ static struct qcom_icc_node xm_ufs_card = { .name = "xm_ufs_card", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -586,6 +718,11 @@ static struct qcom_icc_node alm_gpu_tcu = { .name = "alm_gpu_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa0000 }, + .prio = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -595,6 +732,11 @@ static struct qcom_icc_node alm_pcie_tcu = { .name = "alm_pcie_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa1000 }, + .prio = 3, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -604,6 +746,11 @@ static struct qcom_icc_node alm_sys_tcu = { .name = "alm_sys_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa2000 }, + .prio = 6, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -623,6 +770,11 @@ static struct qcom_icc_node qnm_cmpnoc0 = { .name = "qnm_cmpnoc0", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x1b000, 0x66000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -632,6 +784,11 @@ static struct qcom_icc_node qnm_cmpnoc1 = { .name = "qnm_cmpnoc1", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x24000, 0x6f000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -651,6 +808,11 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 4, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 4, + .port_offsets = { 0x1c000, 0x1d000, 0x67000, 0x68000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -660,6 +822,11 @@ static struct qcom_icc_node qnm_mnoc_hf = { .name = "qnm_mnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x1e000, 0x69000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_llcc, &qns_pcie }, @@ -669,6 +836,11 @@ static struct qcom_icc_node qnm_mnoc_sf = { .name = "qnm_mnoc_sf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x1f000, 0x6a000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -678,6 +850,12 @@ static struct qcom_icc_node qnm_pcie = { .name = "qnm_pcie", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa3000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -687,6 +865,11 @@ static struct qcom_icc_node qnm_snoc_gc = { .name = "qnm_snoc_gc", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa4000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -695,6 +878,11 @@ static struct qcom_icc_node qnm_snoc_sf = { .name = "qnm_snoc_sf", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa5000 }, + .urg_fwd = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, @@ -737,6 +925,11 @@ static struct qcom_icc_node qnm_camnoc_hf = { .name = "qnm_camnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x10000, 0x10180 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -745,6 +938,11 @@ static struct qcom_icc_node qnm_mdp0_0 = { .name = "qnm_mdp0_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -753,6 +951,11 @@ static struct qcom_icc_node qnm_mdp0_1 = { .name = "qnm_mdp0_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16080 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -761,6 +964,11 @@ static struct qcom_icc_node qnm_mdp1_0 = { .name = "qnm_mdp1_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -769,6 +977,11 @@ static struct qcom_icc_node qnm_mdp1_1 = { .name = "qnm_mdp1_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf800 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -785,6 +998,11 @@ static struct qcom_icc_node qnm_rot_0 = { .name = "qnm_rot_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -793,6 +1011,11 @@ static struct qcom_icc_node qnm_rot_1 = { .name = "qnm_rot_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -801,6 +1024,11 @@ static struct qcom_icc_node qnm_video0 = { .name = "qnm_video0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -809,6 +1037,11 @@ static struct qcom_icc_node qnm_video1 = { .name = "qnm_video1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14080 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -817,6 +1050,11 @@ static struct qcom_icc_node qnm_video_cvp = { .name = "qnm_video_cvp", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -825,6 +1063,11 @@ static struct qcom_icc_node qxm_camnoc_icp = { .name = "qxm_camnoc_icp", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -833,6 +1076,11 @@ static struct qcom_icc_node qxm_camnoc_sf = { .name = "qxm_camnoc_sf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -899,6 +1147,11 @@ static struct qcom_icc_node qnm_lpass_noc = { .name = "qnm_lpass_noc", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_sf }, }; @@ -915,6 +1168,12 @@ static struct qcom_icc_node qxm_pimem = { .name = "qxm_pimem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -923,6 +1182,11 @@ static struct qcom_icc_node xm_gic = { .name = "xm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -1996,11 +2260,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_SERVICE_A1NOC] = &srvc_aggre1_noc, }; +static const struct regmap_config sc8280xp_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3af80, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_aggre1_noc = { + .config = &sc8280xp_aggre1_noc_regmap_config, .nodes = aggre1_noc_nodes, .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), .bcms = aggre1_noc_bcms, .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { @@ -2032,11 +2306,21 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = { [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, }; +static const struct regmap_config sc8280xp_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3af80, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_aggre2_noc = { + .config = &sc8280xp_aggre2_noc_regmap_config, .nodes = aggre2_noc_nodes, .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), .bcms = aggre2_noc_bcms, .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const clk_virt_bcms[] = { @@ -2158,7 +2442,16 @@ static struct qcom_icc_node * const config_noc_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config sc8280xp_config_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x2c000, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_config_noc = { + .config = &sc8280xp_config_noc_regmap_config, .nodes = config_noc_nodes, .num_nodes = ARRAY_SIZE(config_noc_nodes), .bcms = config_noc_bcms, @@ -2174,7 +2467,16 @@ static struct qcom_icc_node * const dc_noc_nodes[] = { [SLAVE_GEM_NOC_CFG] = &qns_gemnoc, }; +static const struct regmap_config sc8280xp_dc_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5080, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_dc_noc = { + .config = &sc8280xp_dc_noc_regmap_config, .nodes = dc_noc_nodes, .num_nodes = ARRAY_SIZE(dc_noc_nodes), .bcms = dc_noc_bcms, @@ -2208,7 +2510,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_SERVICE_GEM_NOC] = &srvc_sys_gemnoc, }; +static const struct regmap_config sc8280xp_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xb8400, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_gem_noc = { + .config = &sc8280xp_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, @@ -2231,7 +2542,16 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { [SLAVE_SERVICE_LPASS_AG_NOC] = &srvc_niu_lpass_agnoc, }; +static const struct regmap_config sc8280xp_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf080, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_lpass_ag_noc = { + .config = &sc8280xp_lpass_ag_noc_regmap_config, .nodes = lpass_ag_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), .bcms = lpass_ag_noc_bcms, @@ -2279,7 +2599,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC] = &srvc_mnoc, }; +static const struct regmap_config sc8280xp_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1fa80, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_mmss_noc = { + .config = &sc8280xp_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -2299,7 +2628,16 @@ static struct qcom_icc_node * const nspa_noc_nodes[] = { [SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc, }; +static const struct regmap_config sc8280xp_nspa_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_nspa_noc = { + .config = &sc8280xp_nspa_noc_regmap_config, .nodes = nspa_noc_nodes, .num_nodes = ARRAY_SIZE(nspa_noc_nodes), .bcms = nspa_noc_bcms, @@ -2319,7 +2657,16 @@ static struct qcom_icc_node * const nspb_noc_nodes[] = { [SLAVE_SERVICE_NSPB_NOC] = &service_nspb_noc, }; +static const struct regmap_config sc8280xp_nspb_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_nspb_noc = { + .config = &sc8280xp_nspb_noc_regmap_config, .nodes = nspb_noc_nodes, .num_nodes = ARRAY_SIZE(nspb_noc_nodes), .bcms = nspb_noc_bcms, @@ -2348,7 +2695,16 @@ static struct qcom_icc_node * const system_noc_main_nodes[] = { [SLAVE_SERVICE_SNOC] = &srvc_snoc, }; +static const struct regmap_config sc8280xp_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1a400, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_system_noc_main = { + .config = &sc8280xp_system_noc_regmap_config, .nodes = system_noc_main_nodes, .num_nodes = ARRAY_SIZE(system_noc_main_nodes), .bcms = system_noc_main_bcms, diff --git a/drivers/interconnect/qcom/x1e80100.c b/drivers/interconnect/qcom/x1e80100.c index 2ba2823c7860..8075e0ff2059 100644 --- a/drivers/interconnect/qcom/x1e80100.c +++ b/drivers/interconnect/qcom/x1e80100.c @@ -173,6 +173,13 @@ static struct qcom_icc_node qhm_qspi = { .name = "qhm_qspi", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -181,6 +188,13 @@ static struct qcom_icc_node qhm_qup1 = { .name = "qhm_qup1", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -189,6 +203,13 @@ static struct qcom_icc_node xm_sdc4 = { .name = "xm_sdc4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -197,6 +218,13 @@ static struct qcom_icc_node xm_ufs_mem = { .name = "xm_ufs_mem", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -205,6 +233,13 @@ static struct qcom_icc_node qhm_qup0 = { .name = "qhm_qup0", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -213,6 +248,13 @@ static struct qcom_icc_node qhm_qup2 = { .name = "qhm_qup2", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -221,6 +263,13 @@ static struct qcom_icc_node qxm_crypto = { .name = "qxm_crypto", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -237,6 +286,13 @@ static struct qcom_icc_node xm_qdss_etr_0 = { .name = "xm_qdss_etr_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -245,6 +301,13 @@ static struct qcom_icc_node xm_qdss_etr_1 = { .name = "xm_qdss_etr_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -253,6 +316,13 @@ static struct qcom_icc_node xm_sdc2 = { .name = "xm_sdc2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -337,6 +407,13 @@ static struct qcom_icc_node alm_gpu_tcu = { .name = "alm_gpu_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x271000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -345,6 +422,13 @@ static struct qcom_icc_node alm_pcie_tcu = { .name = "alm_pcie_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x27d000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -353,6 +437,13 @@ static struct qcom_icc_node alm_sys_tcu = { .name = "alm_sys_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x273000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -370,6 +461,13 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 4, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 4, + .port_offsets = { 0x51000, 0x58000, 0xd1000, 0xd8000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -378,6 +476,13 @@ static struct qcom_icc_node qnm_lpass = { .name = "qnm_lpass", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x275000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -387,6 +492,13 @@ static struct qcom_icc_node qnm_mnoc_hf = { .name = "qnm_mnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x53000, 0xd3000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -395,6 +507,13 @@ static struct qcom_icc_node qnm_mnoc_sf = { .name = "qnm_mnoc_sf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x55000, 0xd5000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -403,6 +522,13 @@ static struct qcom_icc_node qnm_nsp_noc = { .name = "qnm_nsp_noc", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x57000, 0xd7000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -412,6 +538,13 @@ static struct qcom_icc_node qnm_pcie = { .name = "qnm_pcie", .channels = 1, .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x277000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -420,6 +553,13 @@ static struct qcom_icc_node qnm_snoc_sf = { .name = "qnm_snoc_sf", .channels = 1, .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x27b000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -429,6 +569,13 @@ static struct qcom_icc_node xm_gic = { .name = "xm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x27f000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -469,6 +616,13 @@ static struct qcom_icc_node qnm_av1_enc = { .name = "qnm_av1_enc", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2f000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -477,6 +631,13 @@ static struct qcom_icc_node qnm_camnoc_hf = { .name = "qnm_camnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x28000, 0x29000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -485,6 +646,13 @@ static struct qcom_icc_node qnm_camnoc_icp = { .name = "qnm_camnoc_icp", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -493,6 +661,13 @@ static struct qcom_icc_node qnm_camnoc_sf = { .name = "qnm_camnoc_sf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x2b000, 0x2c000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -501,6 +676,13 @@ static struct qcom_icc_node qnm_eva = { .name = "qnm_eva", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x33000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -509,6 +691,13 @@ static struct qcom_icc_node qnm_mdp = { .name = "qnm_mdp", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x2d000, 0x2e000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -517,6 +706,13 @@ static struct qcom_icc_node qnm_video = { .name = "qnm_video", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x30000, 0x31000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -525,6 +721,13 @@ static struct qcom_icc_node qnm_video_cv_cpu = { .name = "qnm_video_cv_cpu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x32000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -533,6 +736,13 @@ static struct qcom_icc_node qnm_video_v_cpu = { .name = "qnm_video_v_cpu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x34000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -573,6 +783,13 @@ static struct qcom_icc_node xm_pcie_3 = { .name = "xm_pcie_3", .channels = 1, .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_north_gem_noc }, }; @@ -581,6 +798,13 @@ static struct qcom_icc_node xm_pcie_4 = { .name = "xm_pcie_4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x8000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_north_gem_noc }, }; @@ -589,6 +813,13 @@ static struct qcom_icc_node xm_pcie_5 = { .name = "xm_pcie_5", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_north_gem_noc }, }; @@ -597,6 +828,13 @@ static struct qcom_icc_node xm_pcie_0 = { .name = "xm_pcie_0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -605,6 +843,13 @@ static struct qcom_icc_node xm_pcie_1 = { .name = "xm_pcie_1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -613,6 +858,13 @@ static struct qcom_icc_node xm_pcie_2 = { .name = "xm_pcie_2", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -621,6 +873,13 @@ static struct qcom_icc_node xm_pcie_6a = { .name = "xm_pcie_6a", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -629,6 +888,13 @@ static struct qcom_icc_node xm_pcie_6b = { .name = "xm_pcie_6b", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -653,6 +919,13 @@ static struct qcom_icc_node qnm_gic = { .name = "qnm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_sf }, }; @@ -685,6 +958,13 @@ static struct qcom_icc_node xm_usb2_0 = { .name = "xm_usb2_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x6000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_north_snoc }, }; @@ -693,6 +973,13 @@ static struct qcom_icc_node xm_usb3_mp = { .name = "xm_usb3_mp", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_north_snoc }, }; @@ -701,6 +988,13 @@ static struct qcom_icc_node xm_usb3_0 = { .name = "xm_usb3_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -709,6 +1003,13 @@ static struct qcom_icc_node xm_usb3_1 = { .name = "xm_usb3_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -717,6 +1018,13 @@ static struct qcom_icc_node xm_usb3_2 = { .name = "xm_usb3_2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -725,6 +1033,13 @@ static struct qcom_icc_node xm_usb4_0 = { .name = "xm_usb4_0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -733,6 +1048,13 @@ static struct qcom_icc_node xm_usb4_1 = { .name = "xm_usb4_1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -741,6 +1063,13 @@ static struct qcom_icc_node xm_usb4_2 = { .name = "xm_usb4_2", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -1466,11 +1795,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, }; +static const struct regmap_config x1e80100_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14400, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_aggre1_noc = { + .config = &x1e80100_aggre1_noc_regmap_config, .nodes = aggre1_noc_nodes, .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), .bcms = aggre1_noc_bcms, .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { @@ -1488,7 +1827,16 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = { [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, }; +static const struct regmap_config x1e80100_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c400, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_aggre2_noc = { + .config = &x1e80100_aggre2_noc_regmap_config, .nodes = aggre2_noc_nodes, .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), .bcms = aggre2_noc_bcms, @@ -1573,7 +1921,16 @@ static struct qcom_icc_node * const cnoc_cfg_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config x1e80100_cnoc_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x6600, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_cnoc_cfg = { + .config = &x1e80100_cnoc_cfg_regmap_config, .nodes = cnoc_cfg_nodes, .num_nodes = ARRAY_SIZE(cnoc_cfg_nodes), .bcms = cnoc_cfg_bcms, @@ -1603,7 +1960,16 @@ static struct qcom_icc_node * const cnoc_main_nodes[] = { [SLAVE_PCIE_6B] = &xs_pcie_6b, }; +static const struct regmap_config x1e80100_cnoc_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14400, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_cnoc_main = { + .config = &x1e80100_cnoc_main_regmap_config, .nodes = cnoc_main_nodes, .num_nodes = ARRAY_SIZE(cnoc_main_nodes), .bcms = cnoc_main_bcms, @@ -1633,7 +1999,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, }; +static const struct regmap_config x1e80100_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x311200, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_gem_noc = { + .config = &x1e80100_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, @@ -1648,7 +2023,16 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, }; +static const struct regmap_config x1e80100_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xe080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_lpass_ag_noc = { + .config = &x1e80100_lpass_ag_noc_regmap_config, .nodes = lpass_ag_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), .bcms = lpass_ag_noc_bcms, @@ -1664,7 +2048,16 @@ static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = { [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, }; +static const struct regmap_config x1e80100_lpass_lpiaon_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x19080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_lpass_lpiaon_noc = { + .config = &x1e80100_lpass_lpiaon_noc_regmap_config, .nodes = lpass_lpiaon_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes), .bcms = lpass_lpiaon_noc_bcms, @@ -1679,7 +2072,16 @@ static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, }; +static const struct regmap_config x1e80100_lpass_lpicx_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3a200, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_lpass_lpicx_noc = { + .config = &x1e80100_lpass_lpicx_noc_regmap_config, .nodes = lpass_lpicx_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), .bcms = lpass_lpicx_noc_bcms, @@ -1724,7 +2126,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC] = &srvc_mnoc, }; +static const struct regmap_config x1e80100_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5b800, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_mmss_noc = { + .config = &x1e80100_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -1740,7 +2151,16 @@ static struct qcom_icc_node * const nsp_noc_nodes[] = { [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, }; +static const struct regmap_config x1e80100_nsp_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xe080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_nsp_noc = { + .config = &x1e80100_nsp_noc_regmap_config, .nodes = nsp_noc_nodes, .num_nodes = ARRAY_SIZE(nsp_noc_nodes), .bcms = nsp_noc_bcms, @@ -1757,7 +2177,16 @@ static struct qcom_icc_node * const pcie_center_anoc_nodes[] = { [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, }; +static const struct regmap_config x1e80100_pcie_center_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7000, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_pcie_center_anoc = { + .config = &x1e80100_pcie_center_anoc_regmap_config, .nodes = pcie_center_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_center_anoc_nodes), .bcms = pcie_center_anoc_bcms, @@ -1774,7 +2203,16 @@ static struct qcom_icc_node * const pcie_north_anoc_nodes[] = { [SLAVE_PCIE_NORTH] = &qns_pcie_north_gem_noc, }; +static const struct regmap_config x1e80100_pcie_north_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_pcie_north_anoc = { + .config = &x1e80100_pcie_north_anoc_regmap_config, .nodes = pcie_north_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_north_anoc_nodes), .bcms = pcie_north_anoc_bcms, @@ -1793,7 +2231,16 @@ static struct qcom_icc_node * const pcie_south_anoc_nodes[] = { [SLAVE_PCIE_SOUTH] = &qns_pcie_south_gem_noc, }; +static const struct regmap_config x1e80100_pcie_south_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xd080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_pcie_south_anoc = { + .config = &x1e80100_pcie_south_anoc_regmap_config, .nodes = pcie_south_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_south_anoc_nodes), .bcms = pcie_south_anoc_bcms, @@ -1815,7 +2262,16 @@ static struct qcom_icc_node * const system_noc_nodes[] = { [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, }; +static const struct regmap_config x1e80100_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_system_noc = { + .config = &x1e80100_system_noc_regmap_config, .nodes = system_noc_nodes, .num_nodes = ARRAY_SIZE(system_noc_nodes), .bcms = system_noc_bcms, @@ -1831,7 +2287,16 @@ static struct qcom_icc_node * const usb_center_anoc_nodes[] = { [SLAVE_USB_NOC_SNOC] = &qns_aggre_usb_snoc, }; +static const struct regmap_config x1e80100_usb_center_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8800, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_usb_center_anoc = { + .config = &x1e80100_usb_center_anoc_regmap_config, .nodes = usb_center_anoc_nodes, .num_nodes = ARRAY_SIZE(usb_center_anoc_nodes), .bcms = usb_center_anoc_bcms, @@ -1847,11 +2312,21 @@ static struct qcom_icc_node * const usb_north_anoc_nodes[] = { [SLAVE_AGGRE_USB_NORTH] = &qns_aggre_usb_north_snoc, }; +static const struct regmap_config x1e80100_usb_north_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_usb_north_anoc = { + .config = &x1e80100_usb_north_anoc_regmap_config, .nodes = usb_north_anoc_nodes, .num_nodes = ARRAY_SIZE(usb_north_anoc_nodes), .bcms = usb_north_anoc_bcms, .num_bcms = ARRAY_SIZE(usb_north_anoc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const usb_south_anoc_bcms[] = { @@ -1867,11 +2342,21 @@ static struct qcom_icc_node * const usb_south_anoc_nodes[] = { [SLAVE_AGGRE_USB_SOUTH] = &qns_aggre_usb_south_snoc, }; +static const struct regmap_config x1e80100_usb_south_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_usb_south_anoc = { + .config = &x1e80100_usb_south_anoc_regmap_config, .nodes = usb_south_anoc_nodes, .num_nodes = ARRAY_SIZE(usb_south_anoc_nodes), .bcms = usb_south_anoc_bcms, .num_bcms = ARRAY_SIZE(usb_south_anoc_bcms), + .qos_requires_clocks = true, }; static const struct of_device_id qnoc_of_match[] = { diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index 1bbefc6de708..2169e4b75f98 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -10,6 +10,8 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/kref.h> +#include <linux/rwsem.h> #include <linux/sched.h> #include <linux/tty.h> #include <linux/serial.h> @@ -25,6 +27,8 @@ static const struct tty_operations ipoctal_fops; +static void ipoctal_release(struct kref *kref); + struct ipoctal_channel { struct ipoctal_stats stats; unsigned int nb_bytes; @@ -49,6 +53,10 @@ struct ipoctal { struct tty_driver *tty_drv; u8 __iomem *mem8_space; u8 __iomem *int_space; + struct kref kref; + struct module *carrier_owner; + struct rw_semaphore remove_sem; + bool removed; }; static inline struct ipoctal *chan_to_ipoctal(struct ipoctal_channel *chan, @@ -70,8 +78,14 @@ static void ipoctal_reset_channel(struct ipoctal_channel *channel) static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) { struct ipoctal_channel *channel; + struct ipoctal *ipoctal; channel = dev_get_drvdata(tty->dev); + ipoctal = chan_to_ipoctal(channel, tty->index); + + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed) + return -ENODEV; /* * Enable RX. TX will be enabled when @@ -95,6 +109,7 @@ static int ipoctal_install(struct tty_driver *driver, struct tty_struct *tty) if (res) goto err_put_carrier; + kref_get(&ipoctal->kref); tty->driver_data = channel; return 0; @@ -460,8 +475,13 @@ static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf, size_t count) { struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); size_t char_copied; + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed || !channel->tty_port.xmit_buf) + return 0; + char_copied = ipoctal_copy_write_buffer(channel, buf, count); /* As the IP-OCTAL 485 only supports half duplex, do it manually */ @@ -501,8 +521,13 @@ static void ipoctal_set_termios(struct tty_struct *tty, unsigned char mr2 = 0; unsigned char csr = 0; struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); speed_t baud; + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed) + return; + cflag = tty->termios.c_cflag; /* Disable and reset everything before change the setup */ @@ -631,10 +656,16 @@ static void ipoctal_hangup(struct tty_struct *tty) { unsigned long flags; struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal; if (channel == NULL) return; + ipoctal = chan_to_ipoctal(channel, tty->index); + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed) + return; + spin_lock_irqsave(&channel->lock, flags); channel->nb_bytes = 0; channel->pointer_read = 0; @@ -651,10 +682,16 @@ static void ipoctal_hangup(struct tty_struct *tty) static void ipoctal_shutdown(struct tty_struct *tty) { struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal; if (channel == NULL) return; + ipoctal = chan_to_ipoctal(channel, tty->index); + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed) + return; + ipoctal_reset_channel(channel); tty_port_set_initialized(&channel->tty_port, false); } @@ -664,8 +701,9 @@ static void ipoctal_cleanup(struct tty_struct *tty) struct ipoctal_channel *channel = tty->driver_data; struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); - /* release the carrier driver */ - ipack_put_carrier(ipoctal->dev); + /* release the carrier driver via cached owner */ + module_put(ipoctal->carrier_owner); + kref_put(&ipoctal->kref, ipoctal_release); } static const struct tty_operations ipoctal_fops = { @@ -683,6 +721,13 @@ static const struct tty_operations ipoctal_fops = { .cleanup = ipoctal_cleanup, }; +static void ipoctal_release(struct kref *kref) +{ + struct ipoctal *ipoctal = container_of(kref, struct ipoctal, kref); + + kfree(ipoctal); +} + static int ipoctal_probe(struct ipack_device *dev) { int res; @@ -692,7 +737,11 @@ static int ipoctal_probe(struct ipack_device *dev) if (ipoctal == NULL) return -ENOMEM; + kref_init(&ipoctal->kref); + init_rwsem(&ipoctal->remove_sem); + ipoctal->dev = dev; + ipoctal->carrier_owner = dev->bus->owner; res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot); if (res) goto out_uninst; @@ -701,7 +750,7 @@ static int ipoctal_probe(struct ipack_device *dev) return 0; out_uninst: - kfree(ipoctal); + kref_put(&ipoctal->kref, ipoctal_release); return res; } @@ -709,6 +758,9 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) { int i; + scoped_guard(rwsem_write, &ipoctal->remove_sem) + ipoctal->removed = true; + ipoctal->dev->bus->ops->free_irq(ipoctal->dev); for (i = 0; i < NR_CHANNELS; i++) { @@ -725,7 +777,7 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) tty_unregister_driver(ipoctal->tty_drv); kfree(ipoctal->tty_drv->name); tty_driver_kref_put(ipoctal->tty_drv); - kfree(ipoctal); + kref_put(&ipoctal->kref, ipoctal_release); } static void ipoctal_remove(struct ipack_device *idev) diff --git a/drivers/mailbox/mailbox-mchp-ipc-sbi.c b/drivers/mailbox/mailbox-mchp-ipc-sbi.c index b87bf2fb4b9b..f081f8a9bcf8 100644 --- a/drivers/mailbox/mailbox-mchp-ipc-sbi.c +++ b/drivers/mailbox/mailbox-mchp-ipc-sbi.c @@ -378,6 +378,8 @@ static int mchp_ipc_get_cluster_aggr_irq(struct mchp_ipc_sbi_mbox *ipc) for_each_online_cpu(cpuid) { hartid = cpuid_to_hartid_map(cpuid); irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "hart-%lu", hartid); + if (!irq_name) + return -ENOMEM; ret = platform_get_irq_byname_optional(pdev, irq_name); if (ret <= 0) continue; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 390256ed91f4..7364931dad3a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -57,28 +57,6 @@ config DUMMY_IRQ The sole purpose of this module is to help with debugging of systems on which spurious IRQs would happen on disabled IRQ vector. -config IBM_ASM - tristate "Device driver for IBM RSA service processor" - depends on X86 && PCI && INPUT - depends on SERIAL_8250 || SERIAL_8250=n - help - This option enables device driver support for in-band access to the - IBM RSA (Condor) service processor in eServer xSeries systems. - The ibmasm device driver allows user space application to access - ASM (Advanced Systems Management) functions on the service - processor. The driver is meant to be used in conjunction with - a user space API. - The ibmasm driver also enables the OS to use the UART on the - service processor board as a regular serial port. To make use of - this feature serial driver support (CONFIG_SERIAL_8250) must be - enabled. - - WARNING: This software may not be supported or function - correctly on your IBM server. Please consult the IBM ServerProven - website <https://www-03.ibm.com/systems/info/x86servers/serverproven/compat/us/> - for information on the specific driver level and support statement - for your IBM server. - config IBMVMC tristate "IBM Virtual Management Channel support" depends on PPC_PSERIES @@ -185,19 +163,6 @@ config ENCLOSURE_SERVICES driver (SCSI/ATA) which supports enclosures or a SCSI enclosure device (SES) to use these services. -config SGI_XP - tristate "Support communication between SGI SSIs" - depends on NET - depends on X86_UV && SMP - depends on X86_64 || BROKEN - select SGI_GRU if X86_64 && SMP - help - An SGI machine can be divided into multiple Single System - Images which act independently of each other and have - hardware based memory protection from the others. Enabling - this feature will allow for direct communication between SSIs - based on a network adapter and DMA messaging. - config SMPRO_ERRMON tristate "Ampere Computing SMPro error monitor driver" depends on MFD_SMPRO || COMPILE_TEST @@ -310,27 +275,6 @@ config QCOM_FASTRPC applications DSP processor. Say M if you want to enable this module. -config SGI_GRU - tristate "SGI GRU driver" - depends on X86_UV && SMP - select MMU_NOTIFIER - help - The GRU is a hardware resource located in the system chipset. The GRU - contains memory that can be mmapped into the user address space. - This memory is used to communicate with the GRU to perform functions - such as load/store, scatter/gather, bcopy, AMOs, etc. The GRU is - directly accessed by user instructions using user virtual addresses. - GRU instructions (ex., bcopy) use user virtual addresses for operands. - - If you are not running on a SGI UV system, say N. - -config SGI_GRU_DEBUG - bool "SGI GRU driver debug" - depends on SGI_GRU - help - This option enables additional debugging code for the SGI GRU driver. - If you are unsure, say N. - config APDS9802ALS tristate "Medfield Avago APDS9802 ALS Sensor module" depends on I2C @@ -494,16 +438,6 @@ config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB -config HISI_HIKEY_USB - tristate "USB GPIO Hub on HiSilicon Hikey 960/970 Platform" - depends on (OF && GPIOLIB) || COMPILE_TEST - depends on USB_ROLE_SWITCH - help - If you say yes here this adds support for the on-board USB GPIO hub - found on HiKey 960/970 boards, which is necessary to support - switching between the dual-role USB-C port and the USB-A host ports - using only one USB controller. - config OPEN_DICE tristate "Open Profile for DICE driver" depends on OF_RESERVED_MEM @@ -651,4 +585,5 @@ source "drivers/misc/mchp_pci1xxxx/Kconfig" source "drivers/misc/keba/Kconfig" source "drivers/misc/amd-sbi/Kconfig" source "drivers/misc/rp1/Kconfig" +source "drivers/misc/issei/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index fed47c7672b9..e8d8d5d88c0d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -3,7 +3,6 @@ # Makefile for misc devices that really don't fit anywhere else. # -obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_IBMVMC) += ibmvmc.o obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o @@ -22,8 +21,6 @@ obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o -obj-$(CONFIG_SGI_XP) += sgi-xp/ -obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_SMPRO_ERRMON) += smpro-errmon.o obj-$(CONFIG_SMPRO_MISC) += smpro-misc.o obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o @@ -56,7 +53,6 @@ obj-y += cardreader/ obj-$(CONFIG_PVPANIC) += pvpanic/ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o -obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o obj-$(CONFIG_NTSYNC) += ntsync.o obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o obj-$(CONFIG_OPEN_DICE) += open-dice.o @@ -74,3 +70,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o obj-y += keba/ obj-y += amd-sbi/ obj-$(CONFIG_MISC_RP1) += rp1/ +obj-$(CONFIG_INTEL_SSEI) += issei/ diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c index 469478f7a1d3..a74b0266ea16 100644 --- a/drivers/misc/ad525x_dpot-i2c.c +++ b/drivers/misc/ad525x_dpot-i2c.c @@ -73,38 +73,39 @@ static void ad_dpot_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id ad_dpot_id[] = { - {"ad5258", AD5258_ID}, - {"ad5259", AD5259_ID}, - {"ad5251", AD5251_ID}, - {"ad5252", AD5252_ID}, - {"ad5253", AD5253_ID}, - {"ad5254", AD5254_ID}, - {"ad5255", AD5255_ID}, - {"ad5241", AD5241_ID}, - {"ad5242", AD5242_ID}, - {"ad5243", AD5243_ID}, - {"ad5245", AD5245_ID}, - {"ad5246", AD5246_ID}, - {"ad5247", AD5247_ID}, - {"ad5248", AD5248_ID}, - {"ad5280", AD5280_ID}, - {"ad5282", AD5282_ID}, - {"adn2860", ADN2860_ID}, - {"ad5273", AD5273_ID}, - {"ad5161", AD5161_ID}, - {"ad5171", AD5171_ID}, - {"ad5170", AD5170_ID}, - {"ad5172", AD5172_ID}, - {"ad5173", AD5173_ID}, - {"ad5272", AD5272_ID}, - {"ad5274", AD5274_ID}, - {} + { .name = "ad5258", .driver_data = AD5258_ID }, + { .name = "ad5259", .driver_data = AD5259_ID }, + { .name = "ad5251", .driver_data = AD5251_ID }, + { .name = "ad5252", .driver_data = AD5252_ID }, + { .name = "ad5253", .driver_data = AD5253_ID }, + { .name = "ad5254", .driver_data = AD5254_ID }, + { .name = "ad5255", .driver_data = AD5255_ID }, + { .name = "ad5241", .driver_data = AD5241_ID }, + { .name = "ad5242", .driver_data = AD5242_ID }, + { .name = "ad5243", .driver_data = AD5243_ID }, + { .name = "ad5245", .driver_data = AD5245_ID }, + { .name = "ad5246", .driver_data = AD5246_ID }, + { .name = "ad5247", .driver_data = AD5247_ID }, + { .name = "ad5248", .driver_data = AD5248_ID }, + { .name = "ad5280", .driver_data = AD5280_ID }, + { .name = "ad5282", .driver_data = AD5282_ID }, + { .name = "adn2860", .driver_data = ADN2860_ID }, + { .name = "ad5273", .driver_data = AD5273_ID }, + { .name = "ad5161", .driver_data = AD5161_ID }, + { .name = "ad5171", .driver_data = AD5171_ID }, + { .name = "ad5170", .driver_data = AD5170_ID }, + { .name = "ad5172", .driver_data = AD5172_ID }, + { .name = "ad5173", .driver_data = AD5173_ID }, + { .name = "ad5272", .driver_data = AD5272_ID }, + { .name = "ad5274", .driver_data = AD5274_ID }, + { } }; MODULE_DEVICE_TABLE(i2c, ad_dpot_id); static struct i2c_driver ad_dpot_i2c_driver = { .driver = { .name = "ad_dpot", + .dev_groups = ad_dpot_groups, }, .probe = ad_dpot_i2c_probe, .remove = ad_dpot_i2c_remove, diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c index 263055bda48b..c4c2ec52a83d 100644 --- a/drivers/misc/ad525x_dpot-spi.c +++ b/drivers/misc/ad525x_dpot-spi.c @@ -96,41 +96,42 @@ static void ad_dpot_spi_remove(struct spi_device *spi) } static const struct spi_device_id ad_dpot_spi_id[] = { - {"ad5160", AD5160_ID}, - {"ad5161", AD5161_ID}, - {"ad5162", AD5162_ID}, - {"ad5165", AD5165_ID}, - {"ad5200", AD5200_ID}, - {"ad5201", AD5201_ID}, - {"ad5203", AD5203_ID}, - {"ad5204", AD5204_ID}, - {"ad5206", AD5206_ID}, - {"ad5207", AD5207_ID}, - {"ad5231", AD5231_ID}, - {"ad5232", AD5232_ID}, - {"ad5233", AD5233_ID}, - {"ad5235", AD5235_ID}, - {"ad5260", AD5260_ID}, - {"ad5262", AD5262_ID}, - {"ad5263", AD5263_ID}, - {"ad5290", AD5290_ID}, - {"ad5291", AD5291_ID}, - {"ad5292", AD5292_ID}, - {"ad5293", AD5293_ID}, - {"ad7376", AD7376_ID}, - {"ad8400", AD8400_ID}, - {"ad8402", AD8402_ID}, - {"ad8403", AD8403_ID}, - {"adn2850", ADN2850_ID}, - {"ad5270", AD5270_ID}, - {"ad5271", AD5271_ID}, - {} + { .name = "ad5160", .driver_data = AD5160_ID }, + { .name = "ad5161", .driver_data = AD5161_ID }, + { .name = "ad5162", .driver_data = AD5162_ID }, + { .name = "ad5165", .driver_data = AD5165_ID }, + { .name = "ad5200", .driver_data = AD5200_ID }, + { .name = "ad5201", .driver_data = AD5201_ID }, + { .name = "ad5203", .driver_data = AD5203_ID }, + { .name = "ad5204", .driver_data = AD5204_ID }, + { .name = "ad5206", .driver_data = AD5206_ID }, + { .name = "ad5207", .driver_data = AD5207_ID }, + { .name = "ad5231", .driver_data = AD5231_ID }, + { .name = "ad5232", .driver_data = AD5232_ID }, + { .name = "ad5233", .driver_data = AD5233_ID }, + { .name = "ad5235", .driver_data = AD5235_ID }, + { .name = "ad5260", .driver_data = AD5260_ID }, + { .name = "ad5262", .driver_data = AD5262_ID }, + { .name = "ad5263", .driver_data = AD5263_ID }, + { .name = "ad5290", .driver_data = AD5290_ID }, + { .name = "ad5291", .driver_data = AD5291_ID }, + { .name = "ad5292", .driver_data = AD5292_ID }, + { .name = "ad5293", .driver_data = AD5293_ID }, + { .name = "ad7376", .driver_data = AD7376_ID }, + { .name = "ad8400", .driver_data = AD8400_ID }, + { .name = "ad8402", .driver_data = AD8402_ID }, + { .name = "ad8403", .driver_data = AD8403_ID }, + { .name = "adn2850", .driver_data = ADN2850_ID }, + { .name = "ad5270", .driver_data = AD5270_ID }, + { .name = "ad5271", .driver_data = AD5271_ID }, + { } }; MODULE_DEVICE_TABLE(spi, ad_dpot_spi_id); static struct spi_driver ad_dpot_spi_driver = { .driver = { .name = "ad_dpot", + .dev_groups = ad_dpot_groups, }, .probe = ad_dpot_spi_probe, .remove = ad_dpot_spi_remove, diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index 57bead9fba1b..a4e22fd4a107 100644 --- a/drivers/misc/ad525x_dpot.c +++ b/drivers/misc/ad525x_dpot.c @@ -630,66 +630,132 @@ static struct attribute *ad525x_attributes_commands[] = { NULL }; -static const struct attribute_group ad525x_group_commands = { - .attrs = ad525x_attributes_commands, +static struct attribute *ad525x_attributes[] = { + &dev_attr_rdac0.attr, + &dev_attr_rdac1.attr, + &dev_attr_rdac2.attr, + &dev_attr_rdac3.attr, + &dev_attr_rdac4.attr, + &dev_attr_rdac5.attr, + &dev_attr_eeprom0.attr, + &dev_attr_eeprom1.attr, + &dev_attr_eeprom2.attr, + &dev_attr_eeprom3.attr, + &dev_attr_eeprom4.attr, + &dev_attr_eeprom5.attr, + &dev_attr_tolerance0.attr, + &dev_attr_tolerance1.attr, + &dev_attr_tolerance2.attr, + &dev_attr_tolerance3.attr, + &dev_attr_tolerance4.attr, + &dev_attr_tolerance5.attr, + &dev_attr_otp0.attr, + &dev_attr_otp1.attr, + &dev_attr_otp2.attr, + &dev_attr_otp3.attr, + &dev_attr_otp4.attr, + &dev_attr_otp5.attr, + &dev_attr_otp0en.attr, + &dev_attr_otp1en.attr, + &dev_attr_otp2en.attr, + &dev_attr_otp3en.attr, + &dev_attr_otp4en.attr, + &dev_attr_otp5en.attr, + &dev_attr_inc_all.attr, + &dev_attr_dec_all.attr, + &dev_attr_inc_all_6db.attr, + &dev_attr_dec_all_6db.attr, + NULL }; -static int ad_dpot_add_files(struct device *dev, - unsigned int features, unsigned int rdac) +static int ad525x_attr_index(struct attribute *attr, + const struct attribute * const *attrs) { - int err = sysfs_create_file(&dev->kobj, - dpot_attrib_wipers[rdac]); - if (features & F_CMD_EEP) - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_eeprom[rdac]); - if (features & F_CMD_TOL) - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_tolerance[rdac]); - if (features & F_CMD_OTP) { - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_otp_en[rdac]); - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_otp[rdac]); - } + int i; - if (err) - dev_err(dev, "failed to register sysfs hooks for RDAC%d\n", - rdac); + for (i = 0; attrs[i]; i++) + if (attr == attrs[i]) + return i; - return err; + return -ENOENT; } -static inline void ad_dpot_remove_files(struct device *dev, - unsigned int features, unsigned int rdac) +static bool ad525x_is_command_attr(struct attribute *attr) { - sysfs_remove_file(&dev->kobj, - dpot_attrib_wipers[rdac]); - if (features & F_CMD_EEP) - sysfs_remove_file(&dev->kobj, - dpot_attrib_eeprom[rdac]); - if (features & F_CMD_TOL) - sysfs_remove_file(&dev->kobj, - dpot_attrib_tolerance[rdac]); - if (features & F_CMD_OTP) { - sysfs_remove_file(&dev->kobj, - dpot_attrib_otp_en[rdac]); - sysfs_remove_file(&dev->kobj, - dpot_attrib_otp[rdac]); + int i; + + for (i = 0; ad525x_attributes_commands[i]; i++) { + if (attr == ad525x_attributes_commands[i]) + return true; } + + return false; +} + +static umode_t ad525x_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct dpot_data *data = dev_get_drvdata(dev); + int rdac; + + if (!data) + return 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_wipers); + if (rdac >= 0) + return data->wipers & BIT(rdac) ? attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_eeprom); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_EEP) ? + attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_tolerance); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_TOL) ? + attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_otp); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ? + attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_otp_en); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ? + attr->mode : 0; + + if (ad525x_is_command_attr(attr)) + return data->feat & F_CMD_INC ? attr->mode : 0; + + return attr->mode; } +static const struct attribute_group ad525x_group = { + .attrs = ad525x_attributes, + .is_visible = ad525x_is_visible, +}; + +const struct attribute_group *ad_dpot_groups[] = { + &ad525x_group, + NULL +}; +EXPORT_SYMBOL(ad_dpot_groups); + int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata, unsigned long devid, const char *name) { struct dpot_data *data; - int i, err = 0; + int i; data = kzalloc_obj(struct dpot_data); if (!data) { - err = -ENOMEM; - goto exit; + dev_err(dev, "failed to create client for %s ID 0x%lX\n", + name, devid); + return -ENOMEM; } dev_set_drvdata(dev, data); @@ -705,51 +771,22 @@ int ad_dpot_probe(struct device *dev, data->wipers = DPOT_WIPERS(devid); for (i = DPOT_RDAC0; i < MAX_RDACS; i++) - if (data->wipers & (1 << i)) { - err = ad_dpot_add_files(dev, data->feat, i); - if (err) - goto exit_remove_files; + if (data->wipers & BIT(i)) { /* power-up midscale */ if (data->feat & F_RDACS_WONLY) data->rdac_cache[i] = data->max_pos / 2; } - if (data->feat & F_CMD_INC) - err = sysfs_create_group(&dev->kobj, &ad525x_group_commands); - - if (err) { - dev_err(dev, "failed to register sysfs hooks\n"); - goto exit_free; - } - dev_info(dev, "%s %d-Position Digital Potentiometer registered\n", name, data->max_pos); return 0; - -exit_remove_files: - for (i = DPOT_RDAC0; i < MAX_RDACS; i++) - if (data->wipers & (1 << i)) - ad_dpot_remove_files(dev, data->feat, i); - -exit_free: - kfree(data); - dev_set_drvdata(dev, NULL); -exit: - dev_err(dev, "failed to create client for %s ID 0x%lX\n", - name, devid); - return err; } EXPORT_SYMBOL(ad_dpot_probe); void ad_dpot_remove(struct device *dev) { struct dpot_data *data = dev_get_drvdata(dev); - int i; - - for (i = DPOT_RDAC0; i < MAX_RDACS; i++) - if (data->wipers & (1 << i)) - ad_dpot_remove_files(dev, data->feat, i); kfree(data); } diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h index 72a9d6801937..2e877c89523b 100644 --- a/drivers/misc/ad525x_dpot.h +++ b/drivers/misc/ad525x_dpot.h @@ -10,6 +10,8 @@ #include <linux/types.h> +struct attribute_group; + #define DPOT_CONF(features, wipers, max_pos, uid) \ (((features) << 18) | (((wipers) & 0xFF) << 10) | \ ((max_pos & 0xF) << 6) | (uid & 0x3F)) @@ -210,5 +212,6 @@ struct ad_dpot_bus_data { int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata, unsigned long devid, const char *name); void ad_dpot_remove(struct device *dev); +extern const struct attribute_group *ad_dpot_groups[]; #endif diff --git a/drivers/misc/amd-sbi/Kconfig b/drivers/misc/amd-sbi/Kconfig index 30e7fad7356c..1a96b71f8506 100644 --- a/drivers/misc/amd-sbi/Kconfig +++ b/drivers/misc/amd-sbi/Kconfig @@ -20,3 +20,16 @@ config AMD_SBRMI_HWMON This provides support for RMI device hardware monitoring. If enabled, a hardware monitoring device will be created for each socket in the system. + +config AMD_SBTSI + tristate "AMD side band TSI support" + depends on I3C_OR_I2C + depends on ARM || ARM64 || COMPILE_TEST + select AUXILIARY_BUS + help + Enables support for the AMD SB-TSI (Side Band Temperature Sensor + Interface) driver, which provides access to emulated CPU temperature + sensors on AMD SoCs via an I2C/I3C connected BMC device. + + This driver can also be built as a module. If so, the module will + be called sbtsi. diff --git a/drivers/misc/amd-sbi/Makefile b/drivers/misc/amd-sbi/Makefile index 38eaaa651fd9..ce9321f5c601 100644 --- a/drivers/misc/amd-sbi/Makefile +++ b/drivers/misc/amd-sbi/Makefile @@ -2,3 +2,6 @@ sbrmi-i2c-objs += rmi-i2c.o rmi-core.o sbrmi-i2c-$(CONFIG_AMD_SBRMI_HWMON) += rmi-hwmon.o obj-$(CONFIG_AMD_SBRMI_I2C) += sbrmi-i2c.o +# SBTSI Configuration +sbtsi-objs += tsi.o tsi-core.o +obj-$(CONFIG_AMD_SBTSI) += sbtsi.o diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index d4238ebad3c6..95c9109101b5 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -581,6 +581,8 @@ int create_misc_rmi_device(struct sbrmi_data *data, GFP_KERNEL, "sbrmi-%x", data->dev_static_addr); + if (!data->sbrmi_misc_dev.name) + return -ENOMEM; data->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR; data->sbrmi_misc_dev.fops = &sbrmi_fops; data->sbrmi_misc_dev.parent = dev; @@ -588,6 +590,8 @@ int create_misc_rmi_device(struct sbrmi_data *data, GFP_KERNEL, "sbrmi-%x", data->dev_static_addr); + if (!data->sbrmi_misc_dev.nodename) + return -ENOMEM; data->sbrmi_misc_dev.mode = 0600; return misc_register(&data->sbrmi_misc_dev); diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index 37e5ea83bf97..463a9a757464 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -141,8 +141,8 @@ static void sbrmi_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id sbrmi_id[] = { - {"sbrmi-i2c"}, - {} + { .name = "sbrmi-i2c" }, + { } }; MODULE_DEVICE_TABLE(i2c, sbrmi_id); @@ -226,7 +226,7 @@ static const struct i3c_device_id sbrmi_i3c_id[] = { I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */ I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */ I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */ - {} + { } }; MODULE_DEVICE_TABLE(i3c, sbrmi_i3c_id); diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c new file mode 100644 index 000000000000..5c178702c67a --- /dev/null +++ b/drivers/misc/amd-sbi/tsi-core.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * tsi-core.c - file defining SB-TSI protocols compliant + * AMD SoC device. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <uapi/misc/amd-apml.h> +#include "tsi-core.h" + +static inline struct sbtsi_i3c_priv *to_sbtsi_i3c_priv(struct sbtsi_data *data) +{ + return container_of(data, struct sbtsi_i3c_priv, data); +} + +void sbtsi_data_release(struct kref *kref) +{ + struct sbtsi_data *data = container_of(kref, struct sbtsi_data, kref); + + mutex_destroy(&data->lock); + if (data->is_i3c) + kfree(to_sbtsi_i3c_priv(data)); + else + kfree(data); +} + +/* I2C transfer function */ +static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) +{ + if (is_read) { + int ret = i2c_smbus_read_byte_data(data->client, reg); + + if (ret < 0) + return ret; + *val = ret; + return 0; + } + return i2c_smbus_write_byte_data(data->client, reg, *val); +} + +/* I3C read transfer function */ +static int sbtsi_i3c_read(struct sbtsi_data *data, u8 reg, u8 *val) +{ + struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data); + struct i3c_xfer xfers[2] = { }; + int ret; + + priv->tx[0] = reg; + + /* Write the register address (DMA_TO_DEVICE). */ + xfers[0].rnw = false; + xfers[0].len = 1; + xfers[0].data.out = priv->tx; + + /* Read the data byte into a separate buffer (DMA_FROM_DEVICE). */ + xfers[1].rnw = true; + xfers[1].len = 1; + xfers[1].data.in = &priv->rx; + + ret = i3c_device_do_xfers(data->i3cdev, xfers, 2, I3C_SDR); + if (ret) + return ret; + + *val = priv->rx; + return ret; +} + +/* I3C write transfer function */ +static int sbtsi_i3c_write(struct sbtsi_data *data, u8 reg, u8 val) +{ + struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data); + struct i3c_xfer xfers = { + .rnw = false, + .len = 2, + .data.out = priv->tx, + }; + + priv->tx[0] = reg; + priv->tx[1] = val; + + return i3c_device_do_xfers(data->i3cdev, &xfers, 1, I3C_SDR); +} + +/* Unified transfer function for I2C and I3C access */ +int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) +{ + if (data->is_i3c) + return is_read ? sbtsi_i3c_read(data, reg, val) + : sbtsi_i3c_write(data, reg, *val); + return sbtsi_i2c_xfer(data, reg, val, is_read); +} +EXPORT_SYMBOL_GPL(sbtsi_xfer); + +/* + * The mutex protects against concurrent register transfers to the device + * over the shared bus. + */ +static int sbtsi_xfer_ioctl(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) +{ + guard(sbtsi)(data); + + if (data->detached) + return -ENODEV; + + return sbtsi_xfer(data, reg, val, is_read); +} + +static int apml_tsi_reg_xfer(struct sbtsi_data *data, + struct apml_tsi_xfer_msg __user *arg) +{ + struct apml_tsi_xfer_msg msg = { 0 }; + int ret; + + if (copy_from_user(&msg, arg, sizeof(struct apml_tsi_xfer_msg))) + return -EFAULT; + + /* + * rflag is a boolean direction flag (0 = write, 1 = read). Reject + * any other value so the upper values stay reserved for future + * extensions instead of being silently treated as a read. + */ + if (msg.pad || msg.rflag > 1) + return -EINVAL; + + ret = sbtsi_xfer_ioctl(data, msg.reg_addr, &msg.data_in_out, msg.rflag); + + if (msg.rflag && !ret) { + if (copy_to_user(arg, &msg, sizeof(struct apml_tsi_xfer_msg))) + return -EFAULT; + } + return ret; +} + +static int sbtsi_open(struct inode *inode, struct file *fp) +{ + struct sbtsi_data *data; + + data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev); + scoped_guard(sbtsi, data) { + if (data->detached) + return -ENODEV; + } + + kref_get(&data->kref); + + return 0; +} + +static int sbtsi_release(struct inode *inode, struct file *fp) +{ + struct sbtsi_data *data; + + data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev); + kref_put(&data->kref, sbtsi_data_release); + return 0; +} + +static long sbtsi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct sbtsi_data *data; + + data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev); + switch (cmd) { + case SBTSI_IOCTL_REG_XFER_CMD: + return apml_tsi_reg_xfer(data, argp); + default: + return -ENOTTY; + } +} + +static const struct file_operations sbtsi_fops = { + .owner = THIS_MODULE, + .open = sbtsi_open, + .release = sbtsi_release, + .unlocked_ioctl = sbtsi_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev) +{ + int ret; + + data->sbtsi_misc_dev.name = devm_kasprintf(dev, GFP_KERNEL, + "sbtsi-%x", data->dev_addr); + if (!data->sbtsi_misc_dev.name) + return -ENOMEM; + data->sbtsi_misc_dev.minor = MISC_DYNAMIC_MINOR; + data->sbtsi_misc_dev.fops = &sbtsi_fops; + data->sbtsi_misc_dev.parent = dev; + data->sbtsi_misc_dev.nodename = devm_kasprintf(dev, GFP_KERNEL, + "sbtsi-%x", data->dev_addr); + if (!data->sbtsi_misc_dev.nodename) + return -ENOMEM; + data->sbtsi_misc_dev.mode = 0600; + + ret = misc_register(&data->sbtsi_misc_dev); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/misc/amd-sbi/tsi-core.h b/drivers/misc/amd-sbi/tsi-core.h new file mode 100644 index 000000000000..4cf55c46230e --- /dev/null +++ b/drivers/misc/amd-sbi/tsi-core.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD SBTSI core driver private definitions. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#ifndef _LINUX_TSI_CORE_H_ +#define _LINUX_TSI_CORE_H_ + +#include <linux/cache.h> +#include <linux/misc/tsi.h> + +/** + * struct sbtsi_i3c_priv - per-device state for I3C SBTSI (includes DMA-safe buffers) + * @data: public device state exposed via dev_set_drvdata() + * @tx: outgoing I3C bytes (DMA_TO_DEVICE); [0] register address, [1] value + * @rx: incoming I3C data byte (DMA_FROM_DEVICE) + */ +struct sbtsi_i3c_priv { + struct sbtsi_data data; + u8 tx[2]; + u8 rx __aligned(ARCH_DMA_MINALIGN); +}; + +int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev); + +void sbtsi_data_release(struct kref *kref); +#endif /* _LINUX_TSI_CORE_H_ */ diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c new file mode 100644 index 000000000000..f06f417f451c --- /dev/null +++ b/drivers/misc/amd-sbi/tsi.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * tsi.c - AMD SBTSI I2C/I3C core driver. Probes the SBTSI device over I2C/I3C + * and publishes an auxiliary device on the auxiliary bus. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/slab.h> +#include "tsi-core.h" + +#define SBTSI_REG_CONFIG 0x03 /* RO */ + +/* + * Bit for reporting value with temperature measurement range. + * bit == 0: Use default temperature range (0C to 255.875C). + * bit == 1: Use extended temperature range (-49C to +206.875C). + */ +#define SBTSI_CONFIG_EXT_RANGE_SHIFT 2 + +/* + * ReadOrder bit specifies the reading order of integer and decimal part of + * CPU temperature for atomic reads. If bit == 0, reading integer part triggers + * latching of the decimal part, so integer part should be read first. + */ +#define SBTSI_CONFIG_READ_ORDER_SHIFT 5 + +static void sbtsi_adev_release(struct device *dev) +{ + kfree(to_auxiliary_dev(dev)); +} + +static void sbtsi_unregister_hwmon_adev(void *_adev) +{ + struct auxiliary_device *adev = _adev; + + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); +} + +static void sbtsi_misc_unregister(void *arg) +{ + struct sbtsi_data *data = arg; + + misc_deregister(&data->sbtsi_misc_dev); + + guard(sbtsi)(data); + data->detached = true; +} + +static void sbtsi_driver_unref(void *arg) +{ + struct sbtsi_data *data = arg; + + kref_put(&data->kref, sbtsi_data_release); +} + +/* + * Create and publish an auxiliary device. The hwmon driver in + * drivers/hwmon/sbtsi_temp.c binds to this device. + * + * @dev: I2C device (parent of the auxiliary device) + * @dev_addr: I2C address — used as the auxiliary device instance ID so that + * each socket gets a unique name. + */ +static int sbtsi_create_hwmon_adev(struct device *dev, u8 dev_addr) +{ + struct auxiliary_device *adev; + int ret; + + adev = kzalloc_obj(*adev); + if (!adev) + return -ENOMEM; + + adev->name = AMD_SBTSI_AUX_HWMON; + adev->id = dev_addr; + adev->dev.parent = dev; + adev->dev.release = sbtsi_adev_release; + + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ret; + } + + ret = __auxiliary_device_add(adev, AMD_SBTSI_ADEV); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + return devm_add_action_or_reset(dev, sbtsi_unregister_hwmon_adev, adev); +} + +static int sbtsi_probe_common(struct device *dev, struct sbtsi_data *data) +{ + u8 val; + int err; + + mutex_init(&data->lock); + kref_init(&data->kref); + + err = devm_add_action_or_reset(dev, sbtsi_driver_unref, data); + if (err) + return err; + + err = sbtsi_xfer(data, SBTSI_REG_CONFIG, &val, true); + if (err) + return err; + + data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), val); + data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val); + + dev_set_drvdata(dev, data); + err = sbtsi_create_hwmon_adev(dev, data->dev_addr); + if (err < 0) + return err; + + err = create_misc_tsi_device(data, dev); + if (err) + return err; + + return devm_add_action_or_reset(dev, sbtsi_misc_unregister, data); +} + +static int sbtsi_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct sbtsi_data *data; + + data = kzalloc_obj(*data); + if (!data) + return -ENOMEM; + + data->is_i3c = false; + data->client = client; + + /* In a multi-socket system, devices that are otherwise identical do not + * share the same static address; each instance resides at a unique I2C + * client address on the same or different bus. Use the I2C client + * address as the auxiliary device instance ID to ensure each socket + * receives a distinct auxiliary device name. + */ + data->dev_addr = client->addr; + return sbtsi_probe_common(dev, data); +} + +static const struct i2c_device_id sbtsi_id[] = { + { .name = "sbtsi" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sbtsi_id); + +static const struct of_device_id __maybe_unused sbtsi_of_match[] = { + { + .compatible = "amd,sbtsi", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sbtsi_of_match); + +static struct i2c_driver sbtsi_driver = { + .driver = { + .name = "sbtsi-i2c", + .of_match_table = of_match_ptr(sbtsi_of_match), + }, + .probe = sbtsi_i2c_probe, + .id_table = sbtsi_id, +}; + +static int sbtsi_i3c_probe(struct i3c_device *i3cdev) +{ + struct device *dev = i3cdev_to_dev(i3cdev); + struct i3c_device_info devinfo; + struct sbtsi_i3c_priv *i3c_priv; + struct sbtsi_data *data; + + /* + * AMD OOB devices differ on basis of Instance ID, + * for SBTSI, instance ID is 0. + * As the device Id match is not on basis of Instance ID, + * add the below check to probe the SBTSI device only and + * not other OOB devices. + */ + i3c_device_get_info(i3cdev, &devinfo); + if (I3C_PID_INSTANCE_ID(devinfo.pid) != 0) + return -ENXIO; + + i3c_priv = kzalloc_obj(*i3c_priv); + if (!i3c_priv) + return -ENOMEM; + + data = &i3c_priv->data; + data->i3cdev = i3cdev; + data->is_i3c = true; + /* + * In a multi-socket system, otherwise identical devices do not share + * the same address; each instance is enumerated with a distinct dynamic + * (assigned) address on the I3C bus. Use that address (passed in as + * dev_addr) as the auxiliary device instance ID so that every socket + * gets a unique auxiliary device name. + */ + data->dev_addr = devinfo.dyn_addr; + + return sbtsi_probe_common(dev, data); +} + +static const struct i3c_device_id sbtsi_i3c_id[] = { + /* PID for AMD SBTSI device */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x1, NULL), /* Socket:0, Turin and Genoa */ + I3C_DEVICE_EXTRA_INFO(0x0, 0x0, 0x118, NULL), /* Socket:0, Venice */ + I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */ + {} +}; +MODULE_DEVICE_TABLE(i3c, sbtsi_i3c_id); + +static struct i3c_driver sbtsi_i3c_driver = { + .driver = { + .name = "sbtsi-i3c", + }, + .probe = sbtsi_i3c_probe, + .id_table = sbtsi_i3c_id, +}; + +module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver); + +MODULE_DESCRIPTION("AMD SB-TSI I2C/I3C core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index fc504bd8d916..1488e28819b7 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -285,7 +285,7 @@ static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend, #endif /* CONFIG_PM */ static const struct i2c_device_id apds9802als_id[] = { - { DRIVER_NAME }, + { .name = DRIVER_NAME }, { } }; diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c index 5773ffb46f0f..88ee3a417616 100644 --- a/drivers/misc/bcm-vk/bcm_vk_dev.c +++ b/drivers/misc/bcm-vk/bcm_vk_dev.c @@ -1370,21 +1370,15 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), bcm_vk_msgq_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, vk); - if (err) { - dev_err(dev, "failed to request msgq IRQ %d for MSIX %d\n", - pdev->irq + vk->num_irqs, vk->num_irqs + 1); + if (err) goto err_irq; - } } /* one irq for notification from VK */ err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), bcm_vk_notf_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, vk); - if (err) { - dev_err(dev, "failed to request notf IRQ %d for MSIX %d\n", - pdev->irq + vk->num_irqs, vk->num_irqs + 1); + if (err) goto err_irq; - } vk->num_irqs++; for (i = 0; @@ -1393,11 +1387,8 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), bcm_vk_tty_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, vk); - if (err) { - dev_err(dev, "failed request tty IRQ %d for MSIX %d\n", - pdev->irq + vk->num_irqs, vk->num_irqs + 1); + if (err) goto err_irq; - } bcm_vk_tty_set_irq_enabled(vk, i); } @@ -1632,7 +1623,7 @@ static void bcm_vk_shutdown(struct pci_dev *pdev) } static const struct pci_device_id bcm_vk_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE), }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE) }, { } }; MODULE_DEVICE_TABLE(pci, bcm_vk_ids); diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.c b/drivers/misc/bcm-vk/bcm_vk_msg.c index 3916ec07ecad..17114092a284 100644 --- a/drivers/misc/bcm-vk/bcm_vk_msg.c +++ b/drivers/misc/bcm-vk/bcm_vk_msg.c @@ -9,6 +9,7 @@ #include <linux/interrupt.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/overflow.h> #include <linux/poll.h> #include <linux/sizes.h> #include <linux/spinlock.h> @@ -108,7 +109,8 @@ u32 msgq_avail_space(const struct bcm_vk_msgq __iomem *msgq, bool bcm_vk_drv_access_ok(struct bcm_vk *vk) { - return (!!atomic_read(&vk->msgq_inited)); + /* Pair with the release store after message queue initialization. */ + return !!atomic_read_acquire(&vk->msgq_inited); } void bcm_vk_set_host_alert(struct bcm_vk *vk, u32 bit_mask) @@ -501,7 +503,8 @@ int bcm_vk_sync_msgq(struct bcm_vk *vk, bool force_sync) msgq++; } } - atomic_set(&vk->msgq_inited, 1); + /* Publish message queue info before allowing driver access. */ + atomic_set_release(&vk->msgq_inited, 1); return ret; } @@ -1088,6 +1091,7 @@ ssize_t bcm_vk_write(struct file *p_file, u32 q_num; u32 msg_size; u32 msgq_size; + size_t entry_size; if (!bcm_vk_drv_access_ok(vk)) return -EPERM; @@ -1095,20 +1099,26 @@ ssize_t bcm_vk_write(struct file *p_file, dev_dbg(dev, "Msg count %zu\n", count); /* first, do sanity check where count should be multiple of basic blk */ - if (count & (VK_MSGQ_BLK_SIZE - 1)) { - dev_err(dev, "Failure with size %zu not multiple of %zu\n", + if (!count || count & (VK_MSGQ_BLK_SIZE - 1)) { + dev_err(dev, "Failure with size %zu not a positive multiple of %zu\n", count, VK_MSGQ_BLK_SIZE); rc = -EINVAL; goto write_err; } + if (check_add_overflow(sizeof(*entry), count, &entry_size) || + check_add_overflow(entry_size, vk->ib_sgl_size, &entry_size)) { + rc = -EOVERFLOW; + goto write_err; + } + /* allocate the work entry + buffer for size count and inband sgl */ - entry = kzalloc(sizeof(*entry) + count + vk->ib_sgl_size, - GFP_KERNEL); + entry = kzalloc(entry_size, GFP_KERNEL); if (!entry) { rc = -ENOMEM; goto write_err; } + entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT; /* now copy msg from user space, and then formulate the work entry */ if (copy_from_user(&entry->to_v_msg[0], buf, count)) { @@ -1116,7 +1126,6 @@ ssize_t bcm_vk_write(struct file *p_file, goto write_free_ent; } - entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT; entry->ctx = ctx; /* do a check on the blk size which could not exceed queue space */ @@ -1353,4 +1362,3 @@ void bcm_vk_msg_remove(struct bcm_vk *vk) bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_v_msg_chan, NULL); bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_h_msg_chan, NULL); } - diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 45f8fc69a711..98fc5970b2bb 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -1363,9 +1363,9 @@ static int bh1770_runtime_resume(struct device *dev) #endif static const struct i2c_device_id bh1770_id[] = { - { "bh1770glc" }, - { "sfh7770" }, - {} + { .name = "bh1770glc" }, + { .name = "sfh7770" }, + { } }; MODULE_DEVICE_TABLE(i2c, bh1770_id); diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c index 8e7ea2c9142d..9e0f4f91763e 100644 --- a/drivers/misc/cardreader/alcor_pci.c +++ b/drivers/misc/cardreader/alcor_pci.c @@ -47,7 +47,7 @@ static const struct pci_device_id pci_ids[] = { .driver_data = (kernel_ulong_t)&au6621_cfg }, { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6625), .driver_data = (kernel_ulong_t)&au6625_cfg }, - {}, + { } }; MODULE_DEVICE_TABLE(pci, pci_ids); diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index c4d54ca2fa80..c6e602523538 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1196,6 +1196,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) /* Gating real mcu clock */ err = rtsx_pci_write_register(pcr, RTS5261_FW_CFG1, RTS5261_MCU_CLOCK_GATING, 0); + if (err < 0) + return err; err = rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL, SSC_POWER_DOWN, 0); } else { diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index 2dd212f04fed..8e0d11e59c4e 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c @@ -292,9 +292,8 @@ static void cb710_remove_one(struct pci_dev *pdev) } static const struct pci_device_id cb710_pci_tbl[] = { - { PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH, - PCI_ANY_ID, PCI_ANY_ID, }, - { 0, } + { PCI_DEVICE(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH) }, + { } }; static SIMPLE_DEV_PM_OPS(cb710_pm_ops, cb710_suspend, cb710_resume); diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index cb09e056531a..4500d7d0ea77 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -271,14 +271,14 @@ static void ds1682_remove(struct i2c_client *client) } static const struct i2c_device_id ds1682_id[] = { - { "ds1682" }, + { .name = "ds1682" }, { } }; MODULE_DEVICE_TABLE(i2c, ds1682_id); static const struct of_device_id ds1682_of_match[] = { - { .compatible = "dallas,ds1682", }, - {} + { .compatible = "dallas,ds1682" }, + { } }; MODULE_DEVICE_TABLE(of, ds1682_of_match); diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 4d0ce47aa282..d6af02588c66 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -1,52 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "EEPROM support" -config EEPROM_AT24 - tristate "I2C EEPROMs / RAMs / ROMs from most vendors" - depends on I2C && SYSFS - select NVMEM - select NVMEM_SYSFS - select REGMAP - select REGMAP_I2C - help - Enable this driver to get read/write support to most I2C EEPROMs - and compatible devices like FRAMs, SRAMs, ROMs etc. After you - configure the driver to know about each chip on your target - board. Use these generic chip names, instead of vendor-specific - ones like at24c64, 24lc02 or fm24c04: - - 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, - 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048 - - Unless you like data loss puzzles, always be sure that any chip - you configure as a 24c32 (32 kbit) or larger is NOT really a - 24c16 (16 kbit) or smaller, and vice versa. Marking the chip - as read-only won't help recover from this. Also, if your chip - has any software write-protect mechanism you may want to review the - code to make sure this driver won't turn it on by accident. - - If you use this with an SMBus adapter instead of an I2C adapter, - full functionality is not available. Only smaller devices are - supported (24c16 and below, max 4 kByte). - - This driver can also be built as a module. If so, the module - will be called at24. - -config EEPROM_AT25 - tristate "SPI EEPROMs (FRAMs) from most vendors" - depends on SPI && SYSFS - select NVMEM - select NVMEM_SYSFS - select SPI_MEM - help - Enable this driver to get read/write support to most SPI EEPROMs - and Cypress FRAMs, - after you configure the board init code to know about each eeprom - on your target board. - - This driver can also be built as a module. If so, the module - will be called at25. - config EEPROM_MAX6875 tristate "Maxim MAX6874/5 power supply supervisor" depends on I2C @@ -69,22 +23,6 @@ config EEPROM_93CX6 If unsure, say N. -config EEPROM_93XX46 - tristate "Microwire EEPROM 93XX46 support" - depends on SPI && SYSFS - select REGMAP - select NVMEM - select NVMEM_SYSFS - help - Driver for the microwire EEPROM chipsets 93xx46x. The driver - supports both read and write commands and also the command to - erase the whole EEPROM. - - This driver can also be built as a module. If so, the module - will be called eeprom_93xx46. - - If unsure, say N. - config EEPROM_DIGSY_MTC_CFG bool "DigsyMTC display configuration EEPROMs device" depends on GPIO_MPC5200 && SPI_GPIO @@ -107,35 +45,4 @@ config EEPROM_IDT_89HPESX This driver can also be built as a module. If so, the module will be called idt_89hpesx. -config EEPROM_EE1004 - tristate "SPD EEPROMs on DDR4 memory modules" - depends on I2C && SYSFS - select NVMEM - select NVMEM_SYSFS - help - Enable this driver to get read support to SPD EEPROMs following - the JEDEC EE1004 standard. These are typically found on DDR4 - SDRAM memory modules. - - This driver can also be built as a module. If so, the module - will be called ee1004. - -config EEPROM_M24LR - tristate "STMicroelectronics M24LR RFID/NFC EEPROM support" - depends on I2C && SYSFS - select REGMAP_I2C - select NVMEM - select NVMEM_SYSFS - help - This enables support for STMicroelectronics M24LR RFID/NFC EEPROM - chips. These dual-interface devices expose two I2C addresses: - one for EEPROM memory access and another for control and system - configuration (e.g. UID, password handling). - - This driver provides a sysfs interface for control functions and - integrates with the nvmem subsystem for EEPROM access. - - To compile this driver as a module, choose M here: the - module will be called m24lr. - endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index 8f311fd6a4ce..44a20cf025a1 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1,10 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_EEPROM_AT24) += at24.o -obj-$(CONFIG_EEPROM_AT25) += at25.o obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o -obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o -obj-$(CONFIG_EEPROM_EE1004) += ee1004.o -obj-$(CONFIG_EEPROM_M24LR) += m24lr.o diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index e056d2dea8c3..ea7478cfdd27 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1375,12 +1375,12 @@ static void idt_remove(struct i2c_client *client) * ee_ids - array of supported EEPROMs */ static const struct i2c_device_id ee_ids[] = { - { "24c32", 4096}, - { "24c64", 8192}, - { "24c128", 16384}, - { "24c256", 32768}, - { "24c512", 65536}, - {} + { .name = "24c32", .driver_data = 4096 }, + { .name = "24c64", .driver_data = 8192 }, + { .name = "24c128", .driver_data = 16384 }, + { .name = "24c256", .driver_data = 32768 }, + { .name = "24c512", .driver_data = 65536 }, + { } }; MODULE_DEVICE_TABLE(i2c, ee_ids); @@ -1388,115 +1388,115 @@ MODULE_DEVICE_TABLE(i2c, ee_ids); * idt_ids - supported IDT 89HPESx devices */ static const struct i2c_device_id idt_ids[] = { - { "89hpes8nt2" }, - { "89hpes12nt3" }, - - { "89hpes24nt6ag2" }, - { "89hpes32nt8ag2" }, - { "89hpes32nt8bg2" }, - { "89hpes12nt12g2" }, - { "89hpes16nt16g2" }, - { "89hpes24nt24g2" }, - { "89hpes32nt24ag2" }, - { "89hpes32nt24bg2" }, - - { "89hpes12n3" }, - { "89hpes12n3a" }, - { "89hpes24n3" }, - { "89hpes24n3a" }, - - { "89hpes32h8" }, - { "89hpes32h8g2" }, - { "89hpes48h12" }, - { "89hpes48h12g2" }, - { "89hpes48h12ag2" }, - { "89hpes16h16" }, - { "89hpes22h16" }, - { "89hpes22h16g2" }, - { "89hpes34h16" }, - { "89hpes34h16g2" }, - { "89hpes64h16" }, - { "89hpes64h16g2" }, - { "89hpes64h16ag2" }, - - /* { "89hpes3t3" }, // No SMBus-slave iface */ - { "89hpes12t3g2" }, - { "89hpes24t3g2" }, - /* { "89hpes4t4" }, // No SMBus-slave iface */ - { "89hpes16t4" }, - { "89hpes4t4g2" }, - { "89hpes10t4g2" }, - { "89hpes16t4g2" }, - { "89hpes16t4ag2" }, - { "89hpes5t5" }, - { "89hpes6t5" }, - { "89hpes8t5" }, - { "89hpes8t5a" }, - { "89hpes24t6" }, - { "89hpes6t6g2" }, - { "89hpes24t6g2" }, - { "89hpes16t7" }, - { "89hpes32t8" }, - { "89hpes32t8g2" }, - { "89hpes48t12" }, - { "89hpes48t12g2" }, + { .name = "89hpes8nt2" }, + { .name = "89hpes12nt3" }, + + { .name = "89hpes24nt6ag2" }, + { .name = "89hpes32nt8ag2" }, + { .name = "89hpes32nt8bg2" }, + { .name = "89hpes12nt12g2" }, + { .name = "89hpes16nt16g2" }, + { .name = "89hpes24nt24g2" }, + { .name = "89hpes32nt24ag2" }, + { .name = "89hpes32nt24bg2" }, + + { .name = "89hpes12n3" }, + { .name = "89hpes12n3a" }, + { .name = "89hpes24n3" }, + { .name = "89hpes24n3a" }, + + { .name = "89hpes32h8" }, + { .name = "89hpes32h8g2" }, + { .name = "89hpes48h12" }, + { .name = "89hpes48h12g2" }, + { .name = "89hpes48h12ag2" }, + { .name = "89hpes16h16" }, + { .name = "89hpes22h16" }, + { .name = "89hpes22h16g2" }, + { .name = "89hpes34h16" }, + { .name = "89hpes34h16g2" }, + { .name = "89hpes64h16" }, + { .name = "89hpes64h16g2" }, + { .name = "89hpes64h16ag2" }, + + /* { .name = "89hpes3t3" }, // No SMBus-slave iface */ + { .name = "89hpes12t3g2" }, + { .name = "89hpes24t3g2" }, + /* { .name = "89hpes4t4" }, // No SMBus-slave iface */ + { .name = "89hpes16t4" }, + { .name = "89hpes4t4g2" }, + { .name = "89hpes10t4g2" }, + { .name = "89hpes16t4g2" }, + { .name = "89hpes16t4ag2" }, + { .name = "89hpes5t5" }, + { .name = "89hpes6t5" }, + { .name = "89hpes8t5" }, + { .name = "89hpes8t5a" }, + { .name = "89hpes24t6" }, + { .name = "89hpes6t6g2" }, + { .name = "89hpes24t6g2" }, + { .name = "89hpes16t7" }, + { .name = "89hpes32t8" }, + { .name = "89hpes32t8g2" }, + { .name = "89hpes48t12" }, + { .name = "89hpes48t12g2" }, { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(i2c, idt_ids); static const struct of_device_id idt_of_match[] = { - { .compatible = "idt,89hpes8nt2", }, - { .compatible = "idt,89hpes12nt3", }, - - { .compatible = "idt,89hpes24nt6ag2", }, - { .compatible = "idt,89hpes32nt8ag2", }, - { .compatible = "idt,89hpes32nt8bg2", }, - { .compatible = "idt,89hpes12nt12g2", }, - { .compatible = "idt,89hpes16nt16g2", }, - { .compatible = "idt,89hpes24nt24g2", }, - { .compatible = "idt,89hpes32nt24ag2", }, - { .compatible = "idt,89hpes32nt24bg2", }, - - { .compatible = "idt,89hpes12n3", }, - { .compatible = "idt,89hpes12n3a", }, - { .compatible = "idt,89hpes24n3", }, - { .compatible = "idt,89hpes24n3a", }, - - { .compatible = "idt,89hpes32h8", }, - { .compatible = "idt,89hpes32h8g2", }, - { .compatible = "idt,89hpes48h12", }, - { .compatible = "idt,89hpes48h12g2", }, - { .compatible = "idt,89hpes48h12ag2", }, - { .compatible = "idt,89hpes16h16", }, - { .compatible = "idt,89hpes22h16", }, - { .compatible = "idt,89hpes22h16g2", }, - { .compatible = "idt,89hpes34h16", }, - { .compatible = "idt,89hpes34h16g2", }, - { .compatible = "idt,89hpes64h16", }, - { .compatible = "idt,89hpes64h16g2", }, - { .compatible = "idt,89hpes64h16ag2", }, - - { .compatible = "idt,89hpes12t3g2", }, - { .compatible = "idt,89hpes24t3g2", }, - - { .compatible = "idt,89hpes16t4", }, - { .compatible = "idt,89hpes4t4g2", }, - { .compatible = "idt,89hpes10t4g2", }, - { .compatible = "idt,89hpes16t4g2", }, - { .compatible = "idt,89hpes16t4ag2", }, - { .compatible = "idt,89hpes5t5", }, - { .compatible = "idt,89hpes6t5", }, - { .compatible = "idt,89hpes8t5", }, - { .compatible = "idt,89hpes8t5a", }, - { .compatible = "idt,89hpes24t6", }, - { .compatible = "idt,89hpes6t6g2", }, - { .compatible = "idt,89hpes24t6g2", }, - { .compatible = "idt,89hpes16t7", }, - { .compatible = "idt,89hpes32t8", }, - { .compatible = "idt,89hpes32t8g2", }, - { .compatible = "idt,89hpes48t12", }, - { .compatible = "idt,89hpes48t12g2", }, - { }, + { .compatible = "idt,89hpes8nt2" }, + { .compatible = "idt,89hpes12nt3" }, + + { .compatible = "idt,89hpes24nt6ag2" }, + { .compatible = "idt,89hpes32nt8ag2" }, + { .compatible = "idt,89hpes32nt8bg2" }, + { .compatible = "idt,89hpes12nt12g2" }, + { .compatible = "idt,89hpes16nt16g2" }, + { .compatible = "idt,89hpes24nt24g2" }, + { .compatible = "idt,89hpes32nt24ag2" }, + { .compatible = "idt,89hpes32nt24bg2" }, + + { .compatible = "idt,89hpes12n3" }, + { .compatible = "idt,89hpes12n3a" }, + { .compatible = "idt,89hpes24n3" }, + { .compatible = "idt,89hpes24n3a" }, + + { .compatible = "idt,89hpes32h8" }, + { .compatible = "idt,89hpes32h8g2" }, + { .compatible = "idt,89hpes48h12" }, + { .compatible = "idt,89hpes48h12g2" }, + { .compatible = "idt,89hpes48h12ag2" }, + { .compatible = "idt,89hpes16h16" }, + { .compatible = "idt,89hpes22h16" }, + { .compatible = "idt,89hpes22h16g2" }, + { .compatible = "idt,89hpes34h16" }, + { .compatible = "idt,89hpes34h16g2" }, + { .compatible = "idt,89hpes64h16" }, + { .compatible = "idt,89hpes64h16g2" }, + { .compatible = "idt,89hpes64h16ag2" }, + + { .compatible = "idt,89hpes12t3g2" }, + { .compatible = "idt,89hpes24t3g2" }, + + { .compatible = "idt,89hpes16t4" }, + { .compatible = "idt,89hpes4t4g2" }, + { .compatible = "idt,89hpes10t4g2" }, + { .compatible = "idt,89hpes16t4g2" }, + { .compatible = "idt,89hpes16t4ag2" }, + { .compatible = "idt,89hpes5t5" }, + { .compatible = "idt,89hpes6t5" }, + { .compatible = "idt,89hpes8t5" }, + { .compatible = "idt,89hpes8t5a" }, + { .compatible = "idt,89hpes24t6" }, + { .compatible = "idt,89hpes6t6g2" }, + { .compatible = "idt,89hpes24t6g2" }, + { .compatible = "idt,89hpes16t7" }, + { .compatible = "idt,89hpes32t8" }, + { .compatible = "idt,89hpes32t8g2" }, + { .compatible = "idt,89hpes48t12" }, + { .compatible = "idt,89hpes48t12g2" }, + { } }; MODULE_DEVICE_TABLE(of, idt_of_match); diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index 5731d2dc8a57..be5e6d19be10 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -183,7 +183,7 @@ static void max6875_remove(struct i2c_client *client) } static const struct i2c_device_id max6875_id[] = { - { "max6875" }, + { .name = "max6875" }, { } }; MODULE_DEVICE_TABLE(i2c, max6875_id); diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index cf6382981777..de457378c501 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -184,8 +184,8 @@ EXPORT_SYMBOL_GPL(enclosure_unregister); static void enclosure_link_name(struct enclosure_component *cdev, char *name) { - strcpy(name, "enclosure_device:"); - strcat(name, dev_name(&cdev->cdev)); + snprintf(name, ENCLOSURE_NAME_SIZE, "enclosure_device:%s", + dev_name(&cdev->cdev)); } static void enclosure_remove_links(struct enclosure_component *cdev) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index eb6c2a78d3c7..90fd669636ec 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -22,7 +22,11 @@ #include <linux/firmware/qcom/qcom_scm.h> #include <uapi/misc/fastrpc.h> #include <linux/of_reserved_mem.h> +#include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/bitops.h> +#include <linux/compiler.h> +#include <linux/iopoll.h> #define ADSP_DOMAIN_ID (0) #define MDSP_DOMAIN_ID (1) @@ -37,7 +41,27 @@ #define FASTRPC_CTX_MAX (256) #define FASTRPC_INIT_HANDLE 1 #define FASTRPC_DSP_UTILITIES_HANDLE 2 -#define FASTRPC_CTXID_MASK (0xFF0) +/* + * Maximum handle value for static handles. + * Static handles are pre-defined, fixed numeric values statically assigned + * in the IDL file or FastRPC framework. + */ +#define FASTRPC_MAX_STATIC_HANDLE (20) +#define FASTRPC_CTXID_MASK GENMASK(15, 8) +/* Sequence number occupies bits 63:16 of the ctxid / message context */ +#define FASTRPC_CTXID_SEQ_SHIFT 16 +#define FASTRPC_CTXID_SEQ_MASK GENMASK_ULL(63, 16) + +/* + * Newer DSP firmware implements a PD (Protection Domain) notification + * framework that sends PD state notifications upon request. The PD exit + * notification is unconditionally sent by the DSP with this fixed sentinel + * in the context field rather than the context of an outstanding invocation. + * Since the fastrpc driver does not support the DSP PD notification framework, + * this message must be dropped rather than matched against the context idr. + */ +#define FASTRPC_DSP_PD_NOTIFY_CTX 0xABCDABCD + #define INIT_FILELEN_MAX (2 * 1024 * 1024) #define INIT_FILE_NAMELEN_MAX (128) #define FASTRPC_DEVICE_NAME "fastrpc" @@ -105,6 +129,12 @@ #define miscdev_to_fdevice(d) container_of(d, struct fastrpc_device, miscdev) +/* Poll response number from remote processor for call completion */ +#define FASTRPC_POLL_RESPONSE (0xdecaf) + +/* Polling mode timeout limit */ +#define FASTRPC_POLL_MAX_TIMEOUT_US (10000) + struct fastrpc_phy_page { dma_addr_t addr; /* dma address */ u64 size; /* size of contiguous region */ @@ -233,9 +263,16 @@ struct fastrpc_invoke_ctx { int pid; int client_id; u32 sc; + u64 *fdlist; u32 *crc; + /* Poll memory that DSP updates */ + u32 *poll_addr; u64 ctxid; u64 msg_sz; + /* work done status flag */ + bool is_work_done; + /* process updates poll memory instead of glink response */ + bool is_polled; struct kref refcount; struct list_head node; /* list of ctxs */ struct completion work; @@ -261,6 +298,7 @@ struct fastrpc_soc_data { u32 sid_pos; u32 dma_addr_bits_cdsp; u32 dma_addr_bits_default; + bool poll_mode_supported; }; struct fastrpc_channel_ctx { @@ -283,6 +321,9 @@ struct fastrpc_channel_ctx { struct list_head invoke_interrupted_mmaps; bool secure; bool unsigned_support; + bool poll_mode_supported; + /* Per-channel sequence counter; incremented on every context allocation */ + atomic_t ctx_seq; u64 dma_mask; const struct fastrpc_soc_data *soc_data; }; @@ -306,6 +347,8 @@ struct fastrpc_user { int client_id; int pd; bool is_secure_dev; + /* Flags poll mode state */ + bool poll_mode; /* Lock for lists */ spinlock_t lock; /* lock for allocations */ @@ -422,6 +465,9 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd, static void fastrpc_buf_free(struct fastrpc_buf *buf) { + if (!buf) + return; + dma_free_coherent(buf->dev, buf->size, buf->virt, fastrpc_ipa_to_dma_addr(buf->fl->cctx, buf->dma_addr)); kfree(buf); @@ -515,8 +561,7 @@ static void fastrpc_user_free(struct kref *ref) struct fastrpc_map *map, *m; struct fastrpc_buf *buf, *b; - if (fl->init_mem) - fastrpc_buf_free(fl->init_mem); + fastrpc_buf_free(fl->init_mem); list_for_each_entry_safe(ctx, n, &fl->pending, node) { list_del(&ctx->node); @@ -561,11 +606,10 @@ static void fastrpc_context_free(struct kref *ref) for (i = 0; i < ctx->nbufs; i++) fastrpc_map_put(ctx->maps[i]); - if (ctx->buf) - fastrpc_buf_free(ctx->buf); + fastrpc_buf_free(ctx->buf); spin_lock_irqsave(&cctx->lock, flags); - idr_remove(&cctx->ctx_idr, ctx->ctxid >> 4); + idr_remove(&cctx->ctx_idr, FIELD_GET(FASTRPC_CTXID_MASK, ctx->ctxid)); spin_unlock_irqrestore(&cctx->lock, flags); kfree(ctx->maps); @@ -703,7 +747,9 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc( spin_unlock_irqrestore(&cctx->lock, flags); goto err_idr; } - ctx->ctxid = ret << 4; + ctx->ctxid = FIELD_PREP(FASTRPC_CTXID_MASK, ret) | + FIELD_PREP(FASTRPC_CTXID_SEQ_MASK, + (u64)atomic_inc_return(&cctx->ctx_seq)); spin_unlock_irqrestore(&cctx->lock, flags); kref_init(&ctx->refcount); @@ -968,7 +1014,8 @@ static int fastrpc_get_meta_size(struct fastrpc_invoke_ctx *ctx) sizeof(struct fastrpc_invoke_buf) + sizeof(struct fastrpc_phy_page)) * ctx->nscalars + sizeof(u64) * FASTRPC_MAX_FDLIST + - sizeof(u32) * FASTRPC_MAX_CRCLIST; + sizeof(u32) * FASTRPC_MAX_CRCLIST + + sizeof(u32); return size; } @@ -1063,6 +1110,10 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) rpra = ctx->buf->virt; list = fastrpc_invoke_buf_start(rpra, ctx->nscalars); pages = fastrpc_phy_page_start(list, ctx->nscalars); + ctx->fdlist = (u64 *)(pages + ctx->nscalars); + ctx->poll_addr = (u32 *)((uintptr_t)ctx->fdlist + sizeof(u64) * FASTRPC_MAX_FDLIST + + sizeof(u32) * FASTRPC_MAX_CRCLIST); + args = (uintptr_t)ctx->buf->virt + metalen; rlen = pkt_size - metalen; ctx->rpra = rpra; @@ -1165,18 +1216,11 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx, union fastrpc_remote_arg *rpra = ctx->rpra; struct fastrpc_user *fl = ctx->fl; struct fastrpc_map *mmap = NULL; - struct fastrpc_invoke_buf *list; - struct fastrpc_phy_page *pages; - u64 *fdlist; - int i, inbufs, outbufs, handles; + u64 *fdlist = ctx->fdlist; + int i, inbufs; int ret = 0; inbufs = REMOTE_SCALARS_INBUFS(ctx->sc); - outbufs = REMOTE_SCALARS_OUTBUFS(ctx->sc); - handles = REMOTE_SCALARS_INHANDLES(ctx->sc) + REMOTE_SCALARS_OUTHANDLES(ctx->sc); - list = fastrpc_invoke_buf_start(rpra, ctx->nscalars); - pages = fastrpc_phy_page_start(list, ctx->nscalars); - fdlist = (uint64_t *)(pages + inbufs + outbufs + handles); for (i = inbufs; i < ctx->nbufs; ++i) { if (!ctx->maps[i]) { @@ -1239,6 +1283,71 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx *sctx, } +static u32 fastrpc_read_poll_addr(struct fastrpc_invoke_ctx *ctx) +{ + dma_rmb(); + return READ_ONCE(*ctx->poll_addr); +} + +static int poll_for_remote_response(struct fastrpc_invoke_ctx *ctx) +{ + u32 val; + int ret; + + /* + * Poll until DSP writes FASTRPC_POLL_RESPONSE into *ctx->poll_addr + * or until another path marks the work done. + */ + ret = read_poll_timeout_atomic(fastrpc_read_poll_addr, val, + (val == FASTRPC_POLL_RESPONSE) || ctx->is_work_done, 1, + FASTRPC_POLL_MAX_TIMEOUT_US, false, ctx); + + if (!ret && val == FASTRPC_POLL_RESPONSE) { + /* + * DSP writes FASTRPC_POLL_RESPONSE to signal successful + * completion via the poll path. + */ + ctx->is_work_done = true; + ctx->retval = 0; + } + + if (ret == -ETIMEDOUT) + ret = -EIO; + + return ret; +} + +static inline int fastrpc_wait_for_response(struct fastrpc_invoke_ctx *ctx, + u32 kernel) +{ + int err = 0; + + if (kernel) { + if (!wait_for_completion_timeout(&ctx->work, 10 * HZ)) + err = -ETIMEDOUT; + } else { + err = wait_for_completion_interruptible(&ctx->work); + } + + return err; +} + +static int fastrpc_wait_for_completion(struct fastrpc_invoke_ctx *ctx, + u32 kernel) +{ + int err; + + if (ctx->is_polled) { + err = poll_for_remote_response(ctx); + if (!err) + return 0; + /* If polling timed out or failed, move to normal response mode */ + ctx->is_polled = false; + } + + return fastrpc_wait_for_response(ctx, kernel); +} + static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel, u32 handle, u32 sc, struct fastrpc_invoke_args *args) @@ -1274,13 +1383,14 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel, if (err) goto bail; - if (kernel) { - if (!wait_for_completion_timeout(&ctx->work, 10 * HZ)) - err = -ETIMEDOUT; - } else { - err = wait_for_completion_interruptible(&ctx->work); - } + /* + * Set message context as polled if the call is for a user PD + * dynamic module and user has enabled poll mode. + */ + if (handle > FASTRPC_MAX_STATIC_HANDLE && fl->pd == USER_PD && fl->poll_mode) + ctx->is_polled = true; + err = fastrpc_wait_for_completion(ctx, kernel); if (err) goto bail; @@ -1846,6 +1956,35 @@ done: return 0; } +static int fastrpc_set_option(struct fastrpc_user *fl, char __user *argp) +{ + struct fastrpc_ioctl_set_option opt = {0}; + int i; + + if (copy_from_user(&opt, argp, sizeof(opt))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(opt.reserved); i++) { + if (opt.reserved[i] != 0) + return -EINVAL; + } + + if (opt.request_id != FASTRPC_POLL_MODE) + return -EINVAL; + + if (!fl->cctx->poll_mode_supported) + return -EOPNOTSUPP; + + if (opt.value == FASTRPC_POLL_MODE_ENABLE) + fl->poll_mode = true; + else if (opt.value == FASTRPC_POLL_MODE_DISABLE) + fl->poll_mode = false; + else + return -EINVAL; + + return 0; +} + static int fastrpc_get_dsp_info(struct fastrpc_user *fl, char __user *argp) { struct fastrpc_ioctl_capability cap = {0}; @@ -2207,6 +2346,9 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd, case FASTRPC_IOCTL_MEM_UNMAP: err = fastrpc_req_mem_unmap(fl, argp); break; + case FASTRPC_IOCTL_SET_OPTION: + err = fastrpc_set_option(fl, argp); + break; case FASTRPC_IOCTL_GET_DSP_INFO: err = fastrpc_get_dsp_info(fl, argp); break; @@ -2232,19 +2374,22 @@ static int fastrpc_cb_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int i, sessions = 0; unsigned long flags; - int rc; u32 dma_bits; + u32 sid = 0; + int rc; cctx = dev_get_drvdata(dev->parent); if (!cctx) return -EINVAL; of_property_read_u32(dev->of_node, "qcom,nsessions", &sessions); + if (of_property_read_u32(dev->of_node, "reg", &sid)) + dev_info(dev, "FastRPC Session ID not specified in DT\n"); spin_lock_irqsave(&cctx->lock, flags); if (cctx->sesscount >= FASTRPC_MAX_SESSIONS) { - dev_err(&pdev->dev, "too many sessions\n"); spin_unlock_irqrestore(&cctx->lock, flags); + dev_err(&pdev->dev, "too many sessions\n"); return -ENOSPC; } dma_bits = cctx->soc_data->dma_addr_bits_default; @@ -2253,13 +2398,11 @@ static int fastrpc_cb_probe(struct platform_device *pdev) sess->valid = true; sess->dev = dev; dev_set_drvdata(dev, sess); + sess->sid = sid; if (cctx->domain_id == CDSP_DOMAIN_ID) dma_bits = cctx->soc_data->dma_addr_bits_cdsp; - if (of_property_read_u32(dev->of_node, "reg", &sess->sid)) - dev_info(dev, "FastRPC Session ID not specified in DT\n"); - if (sessions > 0) { struct fastrpc_session_ctx *dup_sess; @@ -2298,8 +2441,8 @@ static void fastrpc_cb_remove(struct platform_device *pdev) } static const struct of_device_id fastrpc_match_table[] = { - { .compatible = "qcom,fastrpc-compute-cb", }, - {} + { .compatible = "qcom,fastrpc-compute-cb" }, + { } }; static struct platform_driver fastrpc_cb_driver = { @@ -2362,6 +2505,7 @@ static const struct fastrpc_soc_data kaanapali_soc_data = { .sid_pos = 56, .dma_addr_bits_cdsp = 34, .dma_addr_bits_default = 32, + .poll_mode_supported = true, }; static const struct fastrpc_soc_data default_soc_data = { @@ -2370,6 +2514,29 @@ static const struct fastrpc_soc_data default_soc_data = { .dma_addr_bits_default = 32, }; +/* + * Exception list for older platforms that use default_soc_data but whose + * DSP firmware supports FastRPC polling mode. + * + * NOTE: This list is intentionally closed. + * Do NOT add new platforms here. New SoCs must advertise polling mode + * support via their soc_data. + */ + +static const struct of_device_id fastrpc_poll_supported_machines[] __maybe_unused = { + { .compatible = "qcom,milos" }, + { .compatible = "qcom,qcs8300" }, + { .compatible = "qcom,sa8775p" }, + { .compatible = "qcom,sar2130p" }, + { .compatible = "qcom,sm8450" }, + { .compatible = "qcom,sm8550" }, + { .compatible = "qcom,sm8650" }, + { .compatible = "qcom,sm8750" }, + { .compatible = "qcom,x1e80100" }, + { .compatible = "qcom,x1p42100" }, + {}, +}; + static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) { struct device *rdev = &rpdev->dev; @@ -2436,6 +2603,8 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain")); data->secure = secure_dsp; data->soc_data = soc_data; + data->poll_mode_supported = soc_data->poll_mode_supported || + of_machine_get_match(fastrpc_poll_supported_machines); switch (domain_id) { case ADSP_DOMAIN_ID: @@ -2465,6 +2634,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) } kref_init(&data->refcount); + atomic_set(&data->ctx_seq, 0); rdev->dma_mask = &data->dma_mask; dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32)); @@ -2551,7 +2721,15 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, if (!cctx) return -ENODEV; - ctxid = ((rsp->ctx & FASTRPC_CTXID_MASK) >> 4); + /* + * A PD exit notification from the DSP PD notification framework carries + * this sentinel rather than a real context. Drop it: a real context is + * (idr_index << 4) | pd and can never collide with this value. + */ + if (rsp->ctx == FASTRPC_DSP_PD_NOTIFY_CTX) + return 0; + + ctxid = FIELD_GET(FASTRPC_CTXID_MASK, rsp->ctx); spin_lock_irqsave(&cctx->lock, flags); ctx = idr_find(&cctx->ctx_idr, ctxid); @@ -2562,7 +2740,25 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, return -ENOENT; } + /* + * Validate the sequence number embedded in the upper bits of the + * context ID. Under high concurrency the IDR slot can be recycled + * for a new request before a late (or duplicate) glink COMPLETE + * response for the previous request arrives. Without this check the + * driver would call complete() on the wrong context, waking a thread + * whose buffers are still being accessed by the DSP. + */ + if (FIELD_GET(FASTRPC_CTXID_SEQ_MASK, rsp->ctx) != + FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid)) { + dev_dbg(&rpdev->dev, + "Stale glink response ctx 0x%llx (expected seq 0x%llx), dropping\n", + rsp->ctx, + FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid)); + return 0; + } + ctx->retval = rsp->retval; + ctx->is_work_done = true; complete(&ctx->work); /* @@ -2578,7 +2774,7 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, static const struct of_device_id fastrpc_rpmsg_of_match[] = { { .compatible = "qcom,kaanapali-fastrpc", .data = &kaanapali_soc_data }, { .compatible = "qcom,fastrpc", .data = &default_soc_data }, - { }, + { } }; MODULE_DEVICE_TABLE(of, fastrpc_rpmsg_of_match); diff --git a/drivers/misc/gehc-achc.c b/drivers/misc/gehc-achc.c index b8fca4d393c6..9792189c0923 100644 --- a/drivers/misc/gehc-achc.c +++ b/drivers/misc/gehc-achc.c @@ -538,8 +538,8 @@ static int gehc_achc_probe(struct spi_device *spi) } static const struct spi_device_id gehc_achc_id[] = { - { "ge,achc", 0 }, - { "achc", 0 }, + { .name = "ge,achc" }, + { .name = "achc" }, { } }; MODULE_DEVICE_TABLE(spi, gehc_achc_id); diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 86bfa82723ff..e602eb3c51fa 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -48,60 +48,42 @@ static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX]; /* PCI structure for identifying device by PCI vendor and device ID */ static const struct pci_device_id genwqe_device_table[] = { - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5, - .class = (PCI_CLASSCODE_GENWQE5 << 8), - .class_mask = ~0, - .driver_data = 0 }, - - /* Initial SR-IOV bring-up image */ - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */ - .device = 0x0000, /* VF Device ID */ - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - /* Fixed up image */ - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */ - .device = 0x0000, /* VF Device ID */ - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - /* Even one more ... */ - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_NEW, - .class = (PCI_CLASSCODE_GENWQE5 << 8), - .class_mask = ~0, - .driver_data = 0 }, - - { 0, } /* 0 terminated list. */ + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5), + .class = PCI_CLASSCODE_GENWQE5 << 8, + .class_mask = ~0, + }, { + /* Initial SR-IOV bring-up image */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM_SRIOV), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000, + PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5_SRIOV), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + /* Fixed up image */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000, + PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + /* Even one more ... */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5_NEW), + .class = (PCI_CLASSCODE_GENWQE5 << 8), + .class_mask = ~0, + }, + { } /* 0 terminated list. */ }; - MODULE_DEVICE_TABLE(pci, genwqe_device_table); /** diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index a2c4a9b4f871..e3b5fb337182 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -413,7 +413,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, size -= size_to_map; map_offs = 0; - if (prev_daddr == daddr) { + if (last_s && prev_daddr == daddr) { u32 prev_len = be32_to_cpu(last_s->len); /* pr_info("daddr combining: " diff --git a/drivers/misc/hi6421v600-irq.c b/drivers/misc/hi6421v600-irq.c index 5ba40222eb12..033f30014aa6 100644 --- a/drivers/misc/hi6421v600-irq.c +++ b/drivers/misc/hi6421v600-irq.c @@ -274,18 +274,15 @@ static int hi6421v600_irq_probe(struct platform_device *pdev) NULL, IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND, "pmic", priv); - if (ret < 0) { - dev_err(dev, "Failed to start IRQ handling thread: error %d\n", - ret); + if (ret < 0) return ret; - } return 0; } static const struct platform_device_id hi6421v600_irq_table[] = { { .name = "hi6421v600-irq" }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, hi6421v600_irq_table); diff --git a/drivers/misc/hisi_hikey_usb.c b/drivers/misc/hisi_hikey_usb.c deleted file mode 100644 index 79f06001259b..000000000000 --- a/drivers/misc/hisi_hikey_usb.c +++ /dev/null @@ -1,274 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Support for usb functionality of Hikey series boards - * based on Hisilicon Kirin Soc. - * - * Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd. - * http://www.huawei.com - * - * Authors: Yu Chen <chenyu56@huawei.com> - */ - -#include <linux/gpio/consumer.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/notifier.h> -#include <linux/platform_device.h> -#include <linux/property.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> -#include <linux/string_choices.h> -#include <linux/usb/role.h> - -#define DEVICE_DRIVER_NAME "hisi_hikey_usb" - -#define HUB_VBUS_POWER_ON 1 -#define HUB_VBUS_POWER_OFF 0 -#define USB_SWITCH_TO_HUB 1 -#define USB_SWITCH_TO_TYPEC 0 -#define TYPEC_VBUS_POWER_ON 1 -#define TYPEC_VBUS_POWER_OFF 0 - -struct hisi_hikey_usb { - struct device *dev; - struct gpio_desc *otg_switch; - struct gpio_desc *typec_vbus; - struct gpio_desc *reset; - - struct regulator *regulator; - - struct usb_role_switch *hub_role_sw; - - struct usb_role_switch *dev_role_sw; - enum usb_role role; - - struct mutex lock; - struct work_struct work; - - struct notifier_block nb; -}; - -static void hub_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, int value) -{ - int ret, status; - - if (!hisi_hikey_usb->regulator) - return; - - status = regulator_is_enabled(hisi_hikey_usb->regulator); - if (status == !!value) - return; - - if (value) - ret = regulator_enable(hisi_hikey_usb->regulator); - else - ret = regulator_disable(hisi_hikey_usb->regulator); - - if (ret) - dev_err(hisi_hikey_usb->dev, - "Can't switch regulator state to %s\n", - str_enabled_disabled(value)); -} - -static void usb_switch_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, - int switch_to) -{ - if (!hisi_hikey_usb->otg_switch) - return; - - gpiod_set_value_cansleep(hisi_hikey_usb->otg_switch, switch_to); -} - -static void usb_typec_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, - int value) -{ - if (!hisi_hikey_usb->typec_vbus) - return; - - gpiod_set_value_cansleep(hisi_hikey_usb->typec_vbus, value); -} - -static void relay_set_role_switch(struct work_struct *work) -{ - struct hisi_hikey_usb *hisi_hikey_usb = container_of(work, - struct hisi_hikey_usb, - work); - struct usb_role_switch *sw; - enum usb_role role; - - if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw) - return; - - mutex_lock(&hisi_hikey_usb->lock); - switch (hisi_hikey_usb->role) { - case USB_ROLE_NONE: - usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF); - usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_HUB); - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_ON); - break; - case USB_ROLE_HOST: - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); - usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC); - usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_ON); - break; - case USB_ROLE_DEVICE: - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); - usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF); - usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC); - break; - default: - break; - } - sw = hisi_hikey_usb->dev_role_sw; - role = hisi_hikey_usb->role; - mutex_unlock(&hisi_hikey_usb->lock); - - usb_role_switch_set_role(sw, role); -} - -static int hub_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role) -{ - struct hisi_hikey_usb *hisi_hikey_usb = usb_role_switch_get_drvdata(sw); - - if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw) - return -EINVAL; - - mutex_lock(&hisi_hikey_usb->lock); - hisi_hikey_usb->role = role; - mutex_unlock(&hisi_hikey_usb->lock); - - schedule_work(&hisi_hikey_usb->work); - - return 0; -} - -static int hisi_hikey_usb_of_role_switch(struct platform_device *pdev, - struct hisi_hikey_usb *hisi_hikey_usb) -{ - struct device *dev = &pdev->dev; - struct usb_role_switch_desc hub_role_switch = {NULL}; - - if (!device_property_read_bool(dev, "usb-role-switch")) - return 0; - - hisi_hikey_usb->otg_switch = devm_gpiod_get(dev, "otg-switch", - GPIOD_OUT_HIGH); - if (IS_ERR(hisi_hikey_usb->otg_switch)) { - dev_err(dev, "get otg-switch failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->otg_switch)); - return PTR_ERR(hisi_hikey_usb->otg_switch); - } - - hisi_hikey_usb->typec_vbus = devm_gpiod_get(dev, "typec-vbus", - GPIOD_OUT_LOW); - if (IS_ERR(hisi_hikey_usb->typec_vbus)) { - dev_err(dev, "get typec-vbus failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->typec_vbus)); - return PTR_ERR(hisi_hikey_usb->typec_vbus); - } - - hisi_hikey_usb->reset = devm_gpiod_get_optional(dev, - "hub-reset-en", - GPIOD_OUT_HIGH); - if (IS_ERR(hisi_hikey_usb->reset)) { - dev_err(dev, "get hub-reset-en failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->reset)); - return PTR_ERR(hisi_hikey_usb->reset); - } - - hisi_hikey_usb->dev_role_sw = usb_role_switch_get(dev); - if (!hisi_hikey_usb->dev_role_sw) - return -EPROBE_DEFER; - if (IS_ERR(hisi_hikey_usb->dev_role_sw)) { - dev_err(dev, "get device role switch failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->dev_role_sw)); - return PTR_ERR(hisi_hikey_usb->dev_role_sw); - } - - INIT_WORK(&hisi_hikey_usb->work, relay_set_role_switch); - - hub_role_switch.fwnode = dev_fwnode(dev); - hub_role_switch.set = hub_usb_role_switch_set; - hub_role_switch.driver_data = hisi_hikey_usb; - - hisi_hikey_usb->hub_role_sw = usb_role_switch_register(dev, - &hub_role_switch); - - if (IS_ERR(hisi_hikey_usb->hub_role_sw)) { - dev_err(dev, - "failed to register hub role with error %ld\n", - PTR_ERR(hisi_hikey_usb->hub_role_sw)); - usb_role_switch_put(hisi_hikey_usb->dev_role_sw); - return PTR_ERR(hisi_hikey_usb->hub_role_sw); - } - - return 0; -} - -static int hisi_hikey_usb_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct hisi_hikey_usb *hisi_hikey_usb; - int ret; - - hisi_hikey_usb = devm_kzalloc(dev, sizeof(*hisi_hikey_usb), GFP_KERNEL); - if (!hisi_hikey_usb) - return -ENOMEM; - - hisi_hikey_usb->dev = &pdev->dev; - mutex_init(&hisi_hikey_usb->lock); - - hisi_hikey_usb->regulator = devm_regulator_get(dev, "hub-vdd"); - if (IS_ERR(hisi_hikey_usb->regulator)) { - if (PTR_ERR(hisi_hikey_usb->regulator) == -EPROBE_DEFER) { - dev_info(dev, "waiting for hub-vdd-supply\n"); - return PTR_ERR(hisi_hikey_usb->regulator); - } - dev_err(dev, "get hub-vdd-supply failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->regulator)); - return PTR_ERR(hisi_hikey_usb->regulator); - } - - ret = hisi_hikey_usb_of_role_switch(pdev, hisi_hikey_usb); - if (ret) - return ret; - - platform_set_drvdata(pdev, hisi_hikey_usb); - - return 0; -} - -static void hisi_hikey_usb_remove(struct platform_device *pdev) -{ - struct hisi_hikey_usb *hisi_hikey_usb = platform_get_drvdata(pdev); - - if (hisi_hikey_usb->hub_role_sw) { - usb_role_switch_unregister(hisi_hikey_usb->hub_role_sw); - - if (hisi_hikey_usb->dev_role_sw) - usb_role_switch_put(hisi_hikey_usb->dev_role_sw); - } else { - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); - } -} - -static const struct of_device_id id_table_hisi_hikey_usb[] = { - { .compatible = "hisilicon,usbhub" }, - {} -}; -MODULE_DEVICE_TABLE(of, id_table_hisi_hikey_usb); - -static struct platform_driver hisi_hikey_usb_driver = { - .probe = hisi_hikey_usb_probe, - .remove = hisi_hikey_usb_remove, - .driver = { - .name = DEVICE_DRIVER_NAME, - .of_match_table = id_table_hisi_hikey_usb, - }, -}; - -module_platform_driver(hisi_hikey_usb_driver); - -MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>"); -MODULE_DESCRIPTION("Driver Support for USB functionality of Hikey"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c index ff92c6edff6b..275dc054ca04 100644 --- a/drivers/misc/hmc6352.c +++ b/drivers/misc/hmc6352.c @@ -121,7 +121,7 @@ static void hmc6352_remove(struct i2c_client *client) } static const struct i2c_device_id hmc6352_id[] = { - { "hmc6352" }, + { .name = "hmc6352" }, { } }; diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index ff3f03ea577e..8988858c2f31 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -33,10 +33,10 @@ static unsigned int max_ccb = 16; static char ilo_hwdev[MAX_ILO_DEV]; static const struct pci_device_id ilo_blacklist[] = { /* auxiliary iLO */ - {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979)}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979) }, /* CL */ - {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289)}, - {} + { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289) }, + { } }; static inline int get_entry_id(int entry) @@ -160,11 +160,16 @@ static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb, ret = fifo_dequeue(hw, fifobar, &entry); if (ret) { + int pkt_len; + pkt_id = get_entry_id(entry); + pkt_len = get_entry_len(entry); + if (pkt_id >= NR_QENTRY || pkt_len > desc_mem_sz(1)) + return 0; if (id) *id = pkt_id; if (len) - *len = get_entry_len(entry); + *len = pkt_len; if (pkt) *pkt = (void *)(desc + desc_mem_sz(pkt_id)); } diff --git a/drivers/misc/ibmasm/Makefile b/drivers/misc/ibmasm/Makefile deleted file mode 100644 index 1b9dd0f44411..000000000000 --- a/drivers/misc/ibmasm/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-$(CONFIG_IBM_ASM) := ibmasm.o - -ibmasm-y := module.o \ - ibmasmfs.o \ - event.o \ - command.o \ - remote.o \ - heartbeat.o \ - r_heartbeat.o \ - dot_command.o \ - lowlevel.o - -ibmasm-$(CONFIG_SERIAL_8250) += uart.o - diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c deleted file mode 100644 index 6b037675ff45..000000000000 --- a/drivers/misc/ibmasm/command.c +++ /dev/null @@ -1,173 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#include <linux/sched.h> -#include <linux/slab.h> -#include "ibmasm.h" -#include "lowlevel.h" - -static void exec_next_command(struct service_processor *sp); - -static atomic_t command_count = ATOMIC_INIT(0); - -struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size) -{ - struct command *cmd; - - if (buffer_size > IBMASM_CMD_MAX_BUFFER_SIZE) - return NULL; - - cmd = kzalloc_obj(struct command); - if (cmd == NULL) - return NULL; - - - cmd->buffer = kzalloc(buffer_size, GFP_KERNEL); - if (cmd->buffer == NULL) { - kfree(cmd); - return NULL; - } - cmd->buffer_size = buffer_size; - - kref_init(&cmd->kref); - cmd->lock = &sp->lock; - - cmd->status = IBMASM_CMD_PENDING; - init_waitqueue_head(&cmd->wait); - INIT_LIST_HEAD(&cmd->queue_node); - - atomic_inc(&command_count); - dbg("command count: %d\n", atomic_read(&command_count)); - - return cmd; -} - -void ibmasm_free_command(struct kref *kref) -{ - struct command *cmd = to_command(kref); - - list_del(&cmd->queue_node); - atomic_dec(&command_count); - dbg("command count: %d\n", atomic_read(&command_count)); - kfree(cmd->buffer); - kfree(cmd); -} - -static void enqueue_command(struct service_processor *sp, struct command *cmd) -{ - list_add_tail(&cmd->queue_node, &sp->command_queue); -} - -static struct command *dequeue_command(struct service_processor *sp) -{ - struct command *cmd; - struct list_head *next; - - if (list_empty(&sp->command_queue)) - return NULL; - - next = sp->command_queue.next; - list_del_init(next); - cmd = list_entry(next, struct command, queue_node); - - return cmd; -} - -static inline void do_exec_command(struct service_processor *sp) -{ - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - - if (ibmasm_send_i2o_message(sp)) { - sp->current_command->status = IBMASM_CMD_FAILED; - wake_up(&sp->current_command->wait); - command_put(sp->current_command); - exec_next_command(sp); - } -} - -/* - * exec_command - * send a command to a service processor - * Commands are executed sequentially. One command (sp->current_command) - * is sent to the service processor. Once the interrupt handler gets a - * message of type command_response, the message is copied into - * the current commands buffer, - */ -void ibmasm_exec_command(struct service_processor *sp, struct command *cmd) -{ - unsigned long flags; - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - - spin_lock_irqsave(&sp->lock, flags); - - if (!sp->current_command) { - sp->current_command = cmd; - command_get(sp->current_command); - spin_unlock_irqrestore(&sp->lock, flags); - do_exec_command(sp); - } else { - enqueue_command(sp, cmd); - spin_unlock_irqrestore(&sp->lock, flags); - } -} - -static void exec_next_command(struct service_processor *sp) -{ - unsigned long flags; - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - - spin_lock_irqsave(&sp->lock, flags); - sp->current_command = dequeue_command(sp); - if (sp->current_command) { - command_get(sp->current_command); - spin_unlock_irqrestore(&sp->lock, flags); - do_exec_command(sp); - } else { - spin_unlock_irqrestore(&sp->lock, flags); - } -} - -/* - * Sleep until a command has failed or a response has been received - * and the command status been updated by the interrupt handler. - * (see receive_response). - */ -void ibmasm_wait_for_response(struct command *cmd, int timeout) -{ - wait_event_interruptible_timeout(cmd->wait, - cmd->status == IBMASM_CMD_COMPLETE || - cmd->status == IBMASM_CMD_FAILED, - timeout * HZ); -} - -/* - * receive_command_response - * called by the interrupt handler when a dot command of type command_response - * was received. - */ -void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size) -{ - struct command *cmd = sp->current_command; - - if (!sp->current_command) - return; - - memcpy_fromio(cmd->buffer, response, min(size, cmd->buffer_size)); - cmd->status = IBMASM_CMD_COMPLETE; - wake_up(&sp->current_command->wait); - command_put(sp->current_command); - exec_next_command(sp); -} diff --git a/drivers/misc/ibmasm/dot_command.c b/drivers/misc/ibmasm/dot_command.c deleted file mode 100644 index df389bd4c9df..000000000000 --- a/drivers/misc/ibmasm/dot_command.c +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#include "ibmasm.h" -#include "dot_command.h" - -/* - * Dispatch an incoming message to the specific handler for the message. - * Called from interrupt context. - */ -void ibmasm_receive_message(struct service_processor *sp, void *message, int message_size) -{ - u32 size; - struct dot_command_header *header = (struct dot_command_header *)message; - - if (message_size == 0) - return; - - size = get_dot_command_size(message); - if (size == 0) - return; - - if (size > message_size) - size = message_size; - - switch (header->type) { - case sp_event: - ibmasm_receive_event(sp, message, size); - break; - case sp_command_response: - ibmasm_receive_command_response(sp, message, size); - break; - case sp_heartbeat: - ibmasm_receive_heartbeat(sp, message, size); - break; - default: - dev_err(sp->dev, "Received unknown message from service processor\n"); - } -} - - -#define INIT_BUFFER_SIZE 32 - - -/* - * send the 4.3.5.10 dot command (driver VPD) to the service processor - */ -int ibmasm_send_driver_vpd(struct service_processor *sp) -{ - struct command *command; - struct dot_command_header *header; - u8 *vpd_command; - u8 *vpd_data; - int result = 0; - - command = ibmasm_new_command(sp, INIT_BUFFER_SIZE); - if (command == NULL) - return -ENOMEM; - - header = (struct dot_command_header *)command->buffer; - header->type = sp_write; - header->command_size = 4; - header->data_size = 16; - header->status = 0; - header->reserved = 0; - - vpd_command = command->buffer + sizeof(struct dot_command_header); - vpd_command[0] = 0x4; - vpd_command[1] = 0x3; - vpd_command[2] = 0x5; - vpd_command[3] = 0xa; - - vpd_data = vpd_command + header->command_size; - vpd_data[0] = 0; - strcat(vpd_data, IBMASM_DRIVER_VPD); - vpd_data[10] = 0; - vpd_data[15] = 0; - - ibmasm_exec_command(sp, command); - ibmasm_wait_for_response(command, IBMASM_CMD_TIMEOUT_NORMAL); - - if (command->status != IBMASM_CMD_COMPLETE) - result = -ENODEV; - - command_put(command); - - return result; -} - -struct os_state_command { - struct dot_command_header header; - unsigned char command[3]; - unsigned char data; -}; - -/* - * send the 4.3.6 dot command (os state) to the service processor - * During driver init this function is called with os state "up". - * This causes the service processor to start sending heartbeats the - * driver. - * During driver exit the function is called with os state "down", - * causing the service processor to stop the heartbeats. - */ -int ibmasm_send_os_state(struct service_processor *sp, int os_state) -{ - struct command *cmd; - struct os_state_command *os_state_cmd; - int result = 0; - - cmd = ibmasm_new_command(sp, sizeof(struct os_state_command)); - if (cmd == NULL) - return -ENOMEM; - - os_state_cmd = (struct os_state_command *)cmd->buffer; - os_state_cmd->header.type = sp_write; - os_state_cmd->header.command_size = 3; - os_state_cmd->header.data_size = 1; - os_state_cmd->header.status = 0; - os_state_cmd->command[0] = 4; - os_state_cmd->command[1] = 3; - os_state_cmd->command[2] = 6; - os_state_cmd->data = os_state; - - ibmasm_exec_command(sp, cmd); - ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL); - - if (cmd->status != IBMASM_CMD_COMPLETE) - result = -ENODEV; - - command_put(cmd); - return result; -} diff --git a/drivers/misc/ibmasm/dot_command.h b/drivers/misc/ibmasm/dot_command.h deleted file mode 100644 index e03399ebe239..000000000000 --- a/drivers/misc/ibmasm/dot_command.h +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#ifndef __DOT_COMMAND_H__ -#define __DOT_COMMAND_H__ - -/* - * dot commands are the protocol used to communicate with the service - * processor. - * They consist of header, a command of variable length and data of - * variable length. - */ - -/* dot command types */ -#define sp_write 0 -#define sp_write_next 1 -#define sp_read 2 -#define sp_read_next 3 -#define sp_command_response 4 -#define sp_event 5 -#define sp_heartbeat 6 - -#pragma pack(1) -struct dot_command_header { - u8 type; - u8 command_size; - u16 data_size; - u8 status; - u8 reserved; -}; -#pragma pack() - -static inline size_t get_dot_command_size(void *buffer) -{ - struct dot_command_header *cmd = (struct dot_command_header *)buffer; - return sizeof(struct dot_command_header) + cmd->command_size + cmd->data_size; -} - -static inline unsigned int get_dot_command_timeout(void *buffer) -{ - struct dot_command_header *header = (struct dot_command_header *)buffer; - unsigned char *cmd = buffer + sizeof(struct dot_command_header); - - /* dot commands 6.3.1, 7.1 and 8.x need a longer timeout */ - - if (header->command_size == 3) { - if ((cmd[0] == 6) && (cmd[1] == 3) && (cmd[2] == 1)) - return IBMASM_CMD_TIMEOUT_EXTRA; - } else if (header->command_size == 2) { - if ((cmd[0] == 7) && (cmd[1] == 1)) - return IBMASM_CMD_TIMEOUT_EXTRA; - if (cmd[0] == 8) - return IBMASM_CMD_TIMEOUT_EXTRA; - } - return IBMASM_CMD_TIMEOUT_NORMAL; -} - -#endif /* __DOT_COMMAND_H__ */ diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c deleted file mode 100644 index 3990bf2b1728..000000000000 --- a/drivers/misc/ibmasm/event.c +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#include <linux/sched.h> -#include <linux/slab.h> -#include "ibmasm.h" -#include "lowlevel.h" - -/* - * ASM service processor event handling routines. - * - * Events are signalled to the device drivers through interrupts. - * They have the format of dot commands, with the type field set to - * sp_event. - * The driver does not interpret the events, it simply stores them in a - * circular buffer. - */ - -static void wake_up_event_readers(struct service_processor *sp) -{ - struct event_reader *reader; - - list_for_each_entry(reader, &sp->event_buffer->readers, node) - wake_up_interruptible(&reader->wait); -} - -/* - * receive_event - * Called by the interrupt handler when a dot command of type sp_event is - * received. - * Store the event in the circular event buffer, wake up any sleeping - * event readers. - * There is no reader marker in the buffer, therefore readers are - * responsible for keeping up with the writer, or they will lose events. - */ -void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size) -{ - struct event_buffer *buffer = sp->event_buffer; - struct ibmasm_event *event; - unsigned long flags; - - data_size = min(data_size, IBMASM_EVENT_MAX_SIZE); - - spin_lock_irqsave(&sp->lock, flags); - /* copy the event into the next slot in the circular buffer */ - event = &buffer->events[buffer->next_index]; - memcpy_fromio(event->data, data, data_size); - event->data_size = data_size; - event->serial_number = buffer->next_serial_number; - - /* advance indices in the buffer */ - buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS; - buffer->next_serial_number++; - spin_unlock_irqrestore(&sp->lock, flags); - - wake_up_event_readers(sp); -} - -static inline int event_available(struct event_buffer *b, struct event_reader *r) -{ - return (r->next_serial_number < b->next_serial_number); -} - -/* - * get_next_event - * Called by event readers (initiated from user space through the file - * system). - * Sleeps until a new event is available. - */ -int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader) -{ - struct event_buffer *buffer = sp->event_buffer; - struct ibmasm_event *event; - unsigned int index; - unsigned long flags; - - reader->cancelled = 0; - - if (wait_event_interruptible(reader->wait, - event_available(buffer, reader) || reader->cancelled)) - return -ERESTARTSYS; - - if (!event_available(buffer, reader)) - return 0; - - spin_lock_irqsave(&sp->lock, flags); - - index = buffer->next_index; - event = &buffer->events[index]; - while (event->serial_number < reader->next_serial_number) { - index = (index + 1) % IBMASM_NUM_EVENTS; - event = &buffer->events[index]; - } - memcpy(reader->data, event->data, event->data_size); - reader->data_size = event->data_size; - reader->next_serial_number = event->serial_number + 1; - - spin_unlock_irqrestore(&sp->lock, flags); - - return event->data_size; -} - -void ibmasm_cancel_next_event(struct event_reader *reader) -{ - reader->cancelled = 1; - wake_up_interruptible(&reader->wait); -} - -void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader) -{ - unsigned long flags; - - reader->next_serial_number = sp->event_buffer->next_serial_number; - init_waitqueue_head(&reader->wait); - spin_lock_irqsave(&sp->lock, flags); - list_add(&reader->node, &sp->event_buffer->readers); - spin_unlock_irqrestore(&sp->lock, flags); -} - -void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader) -{ - unsigned long flags; - - spin_lock_irqsave(&sp->lock, flags); - list_del(&reader->node); - spin_unlock_irqrestore(&sp->lock, flags); -} - -int ibmasm_event_buffer_init(struct service_processor *sp) -{ - struct event_buffer *buffer; - struct ibmasm_event *event; - int i; - - buffer = kmalloc_obj(struct event_buffer); - if (!buffer) - return -ENOMEM; - - buffer->next_index = 0; - buffer->next_serial_number = 1; - - event = buffer->events; - for (i=0; i<IBMASM_NUM_EVENTS; i++, event++) - event->serial_number = 0; - - INIT_LIST_HEAD(&buffer->readers); - - sp->event_buffer = buffer; - - return 0; -} - -void ibmasm_event_buffer_exit(struct service_processor *sp) -{ - kfree(sp->event_buffer); -} diff --git a/drivers/misc/ibmasm/heartbeat.c b/drivers/misc/ibmasm/heartbeat.c deleted file mode 100644 index 59c9a0d95659..000000000000 --- a/drivers/misc/ibmasm/heartbeat.c +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#include <linux/notifier.h> -#include <linux/panic_notifier.h> -#include "ibmasm.h" -#include "dot_command.h" -#include "lowlevel.h" - -static int suspend_heartbeats = 0; - -/* - * Once the driver indicates to the service processor that it is running - * - see send_os_state() - the service processor sends periodic heartbeats - * to the driver. The driver must respond to the heartbeats or else the OS - * will be rebooted. - * In the case of a panic the interrupt handler continues to work and thus - * continues to respond to heartbeats, making the service processor believe - * the OS is still running and thus preventing a reboot. - * To prevent this from happening a callback is added the panic_notifier_list. - * Before responding to a heartbeat the driver checks if a panic has happened, - * if yes it suspends heartbeat, causing the service processor to reboot as - * expected. - */ -static int panic_happened(struct notifier_block *n, unsigned long val, void *v) -{ - suspend_heartbeats = 1; - return 0; -} - -static struct notifier_block panic_notifier = { panic_happened, NULL, 1 }; - -void ibmasm_register_panic_notifier(void) -{ - atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier); -} - -void ibmasm_unregister_panic_notifier(void) -{ - atomic_notifier_chain_unregister(&panic_notifier_list, - &panic_notifier); -} - - -int ibmasm_heartbeat_init(struct service_processor *sp) -{ - sp->heartbeat = ibmasm_new_command(sp, HEARTBEAT_BUFFER_SIZE); - if (sp->heartbeat == NULL) - return -ENOMEM; - - return 0; -} - -void ibmasm_heartbeat_exit(struct service_processor *sp) -{ - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - ibmasm_wait_for_response(sp->heartbeat, IBMASM_CMD_TIMEOUT_NORMAL); - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - suspend_heartbeats = 1; - command_put(sp->heartbeat); -} - -void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size) -{ - struct command *cmd = sp->heartbeat; - struct dot_command_header *header = (struct dot_command_header *)cmd->buffer; - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - if (suspend_heartbeats) - return; - - /* return the received dot command to sender */ - cmd->status = IBMASM_CMD_PENDING; - size = min(size, cmd->buffer_size); - memcpy_fromio(cmd->buffer, message, size); - header->type = sp_write; - ibmasm_exec_command(sp, cmd); -} diff --git a/drivers/misc/ibmasm/i2o.h b/drivers/misc/ibmasm/i2o.h deleted file mode 100644 index 468fa84a3257..000000000000 --- a/drivers/misc/ibmasm/i2o.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#pragma pack(1) -struct i2o_header { - u8 version; - u8 message_flags; - u16 message_size; - u8 target; - u8 initiator_and_target; - u8 initiator; - u8 function; - u32 initiator_context; -}; -#pragma pack() - -#define I2O_HEADER_TEMPLATE \ - { .version = 0x01, \ - .message_flags = 0x00, \ - .function = 0xFF, \ - .initiator = 0x00, \ - .initiator_and_target = 0x40, \ - .target = 0x00, \ - .initiator_context = 0x0 } - -#define I2O_MESSAGE_SIZE 0x1000 -#define I2O_COMMAND_SIZE (I2O_MESSAGE_SIZE - sizeof(struct i2o_header)) - -#pragma pack(1) -struct i2o_message { - struct i2o_header header; - void *data; -}; -#pragma pack() - -static inline unsigned short outgoing_message_size(unsigned int data_size) -{ - unsigned int size; - unsigned short i2o_size; - - if (data_size > I2O_COMMAND_SIZE) - data_size = I2O_COMMAND_SIZE; - - size = sizeof(struct i2o_header) + data_size; - - i2o_size = size / sizeof(u32); - - if (size % sizeof(u32)) - i2o_size++; - - return i2o_size; -} - -static inline u32 incoming_data_size(struct i2o_message *i2o_message) -{ - return (sizeof(u32) * i2o_message->header.message_size); -} diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h deleted file mode 100644 index a5ced88ca923..000000000000 --- a/drivers/misc/ibmasm/ibmasm.h +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/list.h> -#include <linux/wait.h> -#include <linux/spinlock.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/kref.h> -#include <linux/device.h> -#include <linux/input.h> -#include <linux/time64.h> - -/* Driver identification */ -#define DRIVER_NAME "ibmasm" -#define DRIVER_VERSION "1.0" -#define DRIVER_AUTHOR "Max Asbock <masbock@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>" -#define DRIVER_DESC "IBM ASM Service Processor Driver" - -#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME) -#define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME) - -extern int ibmasm_debug; -#define dbg(STR, ARGS...) \ - do { \ - if (ibmasm_debug) \ - printk(KERN_DEBUG STR , ##ARGS); \ - } while (0) - -static inline char *get_timestamp(char *buf) -{ - struct timespec64 now; - - ktime_get_real_ts64(&now); - sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec, - now.tv_nsec / NSEC_PER_USEC); - return buf; -} - -#define IBMASM_CMD_PENDING 0 -#define IBMASM_CMD_COMPLETE 1 -#define IBMASM_CMD_FAILED 2 - -#define IBMASM_CMD_TIMEOUT_NORMAL 45 -#define IBMASM_CMD_TIMEOUT_EXTRA 240 - -#define IBMASM_CMD_MAX_BUFFER_SIZE 0x8000 - -#define REVERSE_HEARTBEAT_TIMEOUT 120 - -#define HEARTBEAT_BUFFER_SIZE 0x400 - -#ifdef IA64 -#define IBMASM_DRIVER_VPD "Lin64 6.08 " -#else -#define IBMASM_DRIVER_VPD "Lin32 6.08 " -#endif - -#define SYSTEM_STATE_OS_UP 5 -#define SYSTEM_STATE_OS_DOWN 4 - -#define IBMASM_NAME_SIZE 16 - -#define IBMASM_NUM_EVENTS 10 -#define IBMASM_EVENT_MAX_SIZE 2048u - - -struct command { - struct list_head queue_node; - wait_queue_head_t wait; - unsigned char *buffer; - size_t buffer_size; - int status; - struct kref kref; - spinlock_t *lock; -}; -#define to_command(c) container_of(c, struct command, kref) - -void ibmasm_free_command(struct kref *kref); -static inline void command_put(struct command *cmd) -{ - unsigned long flags; - spinlock_t *lock = cmd->lock; - - spin_lock_irqsave(lock, flags); - kref_put(&cmd->kref, ibmasm_free_command); - spin_unlock_irqrestore(lock, flags); -} - -static inline void command_get(struct command *cmd) -{ - kref_get(&cmd->kref); -} - - -struct ibmasm_event { - unsigned int serial_number; - unsigned int data_size; - unsigned char data[IBMASM_EVENT_MAX_SIZE]; -}; - -struct event_buffer { - struct ibmasm_event events[IBMASM_NUM_EVENTS]; - unsigned int next_serial_number; - unsigned int next_index; - struct list_head readers; -}; - -struct event_reader { - int cancelled; - unsigned int next_serial_number; - wait_queue_head_t wait; - struct list_head node; - unsigned int data_size; - unsigned char data[IBMASM_EVENT_MAX_SIZE]; -}; - -struct reverse_heartbeat { - wait_queue_head_t wait; - unsigned int stopped; -}; - -struct ibmasm_remote { - struct input_dev *keybd_dev; - struct input_dev *mouse_dev; -}; - -struct service_processor { - struct list_head node; - spinlock_t lock; - void __iomem *base_address; - unsigned int irq; - struct command *current_command; - struct command *heartbeat; - struct list_head command_queue; - struct event_buffer *event_buffer; - char dirname[IBMASM_NAME_SIZE]; - char devname[IBMASM_NAME_SIZE]; - unsigned int number; - struct ibmasm_remote remote; - int serial_line; - struct device *dev; -}; - -/* command processing */ -struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size); -void ibmasm_exec_command(struct service_processor *sp, struct command *cmd); -void ibmasm_wait_for_response(struct command *cmd, int timeout); -void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size); - -/* event processing */ -int ibmasm_event_buffer_init(struct service_processor *sp); -void ibmasm_event_buffer_exit(struct service_processor *sp); -void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size); -void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader); -void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader); -int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader); -void ibmasm_cancel_next_event(struct event_reader *reader); - -/* heartbeat - from SP to OS */ -void ibmasm_register_panic_notifier(void); -void ibmasm_unregister_panic_notifier(void); -int ibmasm_heartbeat_init(struct service_processor *sp); -void ibmasm_heartbeat_exit(struct service_processor *sp); -void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size); - -/* reverse heartbeat - from OS to SP */ -void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); -int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); -void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb); - -/* dot commands */ -void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size); -int ibmasm_send_driver_vpd(struct service_processor *sp); -int ibmasm_send_os_state(struct service_processor *sp, int os_state); - -/* low level message processing */ -int ibmasm_send_i2o_message(struct service_processor *sp); -irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id); - -/* remote console */ -void ibmasm_handle_mouse_interrupt(struct service_processor *sp); -int ibmasm_init_remote_input_dev(struct service_processor *sp); -void ibmasm_free_remote_input_dev(struct service_processor *sp); - -/* file system */ -int ibmasmfs_register(void); -void ibmasmfs_unregister(void); -void ibmasmfs_add_sp(struct service_processor *sp); - -/* uart */ -#if IS_ENABLED(CONFIG_SERIAL_8250) -void ibmasm_register_uart(struct service_processor *sp); -void ibmasm_unregister_uart(struct service_processor *sp); -#else -#define ibmasm_register_uart(sp) do { } while(0) -#define ibmasm_unregister_uart(sp) do { } while(0) -#endif diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c deleted file mode 100644 index dfdfa9ba4747..000000000000 --- a/drivers/misc/ibmasm/ibmasmfs.c +++ /dev/null @@ -1,604 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -/* - * Parts of this code are based on an article by Jonathan Corbet - * that appeared in Linux Weekly News. - */ - - -/* - * The IBMASM file virtual filesystem. It creates the following hierarchy - * dynamically when mounted from user space: - * - * /ibmasm - * |-- 0 - * | |-- command - * | |-- event - * | |-- reverse_heartbeat - * | `-- remote_video - * | |-- depth - * | |-- height - * | `-- width - * . - * . - * . - * `-- n - * |-- command - * |-- event - * |-- reverse_heartbeat - * `-- remote_video - * |-- depth - * |-- height - * `-- width - * - * For each service processor the following files are created: - * - * command: execute dot commands - * write: execute a dot command on the service processor - * read: return the result of a previously executed dot command - * - * events: listen for service processor events - * read: sleep (interruptible) until an event occurs - * write: wakeup sleeping event listener - * - * reverse_heartbeat: send a heartbeat to the service processor - * read: sleep (interruptible) until the reverse heartbeat fails - * write: wakeup sleeping heartbeat listener - * - * remote_video/width - * remote_video/height - * remote_video/width: control remote display settings - * write: set value - * read: read value - */ - -#include <linux/fs.h> -#include <linux/fs_context.h> -#include <linux/pagemap.h> -#include <linux/slab.h> -#include <linux/uaccess.h> -#include <asm/io.h> -#include "ibmasm.h" -#include "remote.h" -#include "dot_command.h" - -#define IBMASMFS_MAGIC 0x66726f67 - -static LIST_HEAD(service_processors); - -static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode); -static void ibmasmfs_create_files (struct super_block *sb); -static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc); - -static int ibmasmfs_get_tree(struct fs_context *fc) -{ - return get_tree_single(fc, ibmasmfs_fill_super); -} - -static const struct fs_context_operations ibmasmfs_context_ops = { - .get_tree = ibmasmfs_get_tree, -}; - -static int ibmasmfs_init_fs_context(struct fs_context *fc) -{ - fc->ops = &ibmasmfs_context_ops; - return 0; -} - -static const struct super_operations ibmasmfs_s_ops = { - .statfs = simple_statfs, - .drop_inode = inode_just_drop, -}; - -static struct file_system_type ibmasmfs_type = { - .owner = THIS_MODULE, - .name = "ibmasmfs", - .init_fs_context = ibmasmfs_init_fs_context, - .kill_sb = kill_anon_super, -}; -MODULE_ALIAS_FS("ibmasmfs"); - -static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc) -{ - struct inode *root; - - sb->s_blocksize = PAGE_SIZE; - sb->s_blocksize_bits = PAGE_SHIFT; - sb->s_magic = IBMASMFS_MAGIC; - sb->s_op = &ibmasmfs_s_ops; - sb->s_time_gran = 1; - - root = ibmasmfs_make_inode (sb, S_IFDIR | 0500); - if (!root) - return -ENOMEM; - - root->i_op = &simple_dir_inode_operations; - root->i_fop = &simple_dir_operations; - - sb->s_root = d_make_root(root); - if (!sb->s_root) - return -ENOMEM; - - ibmasmfs_create_files(sb); - return 0; -} - -static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode) -{ - struct inode *ret = new_inode(sb); - - if (ret) { - ret->i_ino = get_next_ino(); - ret->i_mode = mode; - simple_inode_init_ts(ret); - } - return ret; -} - -static int ibmasmfs_create_file(struct dentry *parent, - const char *name, - const struct file_operations *fops, - void *data, - int mode) -{ - struct dentry *dentry; - struct inode *inode; - - dentry = d_alloc_name(parent, name); - if (!dentry) - return -ENOMEM; - - inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode); - if (!inode) { - dput(dentry); - return -ENOMEM; - } - - inode->i_fop = fops; - inode->i_private = data; - - d_make_persistent(dentry, inode); - dput(dentry); - return 0; -} - -static struct dentry *ibmasmfs_create_dir(struct dentry *parent, - const char *name) -{ - struct dentry *dentry; - struct inode *inode; - - dentry = d_alloc_name(parent, name); - if (!dentry) - return NULL; - - inode = ibmasmfs_make_inode(parent->d_sb, S_IFDIR | 0500); - if (!inode) { - dput(dentry); - return NULL; - } - - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; - - d_make_persistent(dentry, inode); - dput(dentry); - return dentry; // borrowed -} - -int ibmasmfs_register(void) -{ - return register_filesystem(&ibmasmfs_type); -} - -void ibmasmfs_unregister(void) -{ - unregister_filesystem(&ibmasmfs_type); -} - -void ibmasmfs_add_sp(struct service_processor *sp) -{ - list_add(&sp->node, &service_processors); -} - -/* struct to save state between command file operations */ -struct ibmasmfs_command_data { - struct service_processor *sp; - struct command *command; -}; - -/* struct to save state between event file operations */ -struct ibmasmfs_event_data { - struct service_processor *sp; - struct event_reader reader; - int active; -}; - -/* struct to save state between reverse heartbeat file operations */ -struct ibmasmfs_heartbeat_data { - struct service_processor *sp; - struct reverse_heartbeat heartbeat; - int active; -}; - -static int command_file_open(struct inode *inode, struct file *file) -{ - struct ibmasmfs_command_data *command_data; - - if (!inode->i_private) - return -ENODEV; - - command_data = kmalloc_obj(struct ibmasmfs_command_data); - if (!command_data) - return -ENOMEM; - - command_data->command = NULL; - command_data->sp = inode->i_private; - file->private_data = command_data; - return 0; -} - -static int command_file_close(struct inode *inode, struct file *file) -{ - struct ibmasmfs_command_data *command_data = file->private_data; - - if (command_data->command) - command_put(command_data->command); - - kfree(command_data); - return 0; -} - -static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_command_data *command_data = file->private_data; - struct command *cmd; - int len; - unsigned long flags; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) - return 0; - if (*offset != 0) - return 0; - - spin_lock_irqsave(&command_data->sp->lock, flags); - cmd = command_data->command; - if (cmd == NULL) { - spin_unlock_irqrestore(&command_data->sp->lock, flags); - return 0; - } - command_data->command = NULL; - spin_unlock_irqrestore(&command_data->sp->lock, flags); - - if (cmd->status != IBMASM_CMD_COMPLETE) { - command_put(cmd); - return -EIO; - } - len = min(count, cmd->buffer_size); - if (copy_to_user(buf, cmd->buffer, len)) { - command_put(cmd); - return -EFAULT; - } - command_put(cmd); - - return len; -} - -static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) -{ - struct ibmasmfs_command_data *command_data = file->private_data; - struct command *cmd; - unsigned long flags; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) - return 0; - if (count < sizeof(struct dot_command_header)) - return -EINVAL; - if (*offset != 0) - return 0; - - /* commands are executed sequentially, only one command at a time */ - if (command_data->command) - return -EAGAIN; - - cmd = ibmasm_new_command(command_data->sp, count); - if (!cmd) - return -ENOMEM; - - if (copy_from_user(cmd->buffer, ubuff, count)) { - command_put(cmd); - return -EFAULT; - } - - if (count < get_dot_command_size(cmd->buffer)) { - command_put(cmd); - return -EINVAL; - } - - spin_lock_irqsave(&command_data->sp->lock, flags); - if (command_data->command) { - spin_unlock_irqrestore(&command_data->sp->lock, flags); - command_put(cmd); - return -EAGAIN; - } - command_data->command = cmd; - spin_unlock_irqrestore(&command_data->sp->lock, flags); - - ibmasm_exec_command(command_data->sp, cmd); - ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer)); - - return count; -} - -static int event_file_open(struct inode *inode, struct file *file) -{ - struct ibmasmfs_event_data *event_data; - struct service_processor *sp; - - if (!inode->i_private) - return -ENODEV; - - sp = inode->i_private; - - event_data = kmalloc_obj(struct ibmasmfs_event_data); - if (!event_data) - return -ENOMEM; - - ibmasm_event_reader_register(sp, &event_data->reader); - - event_data->sp = sp; - event_data->active = 0; - file->private_data = event_data; - return 0; -} - -static int event_file_close(struct inode *inode, struct file *file) -{ - struct ibmasmfs_event_data *event_data = file->private_data; - - ibmasm_event_reader_unregister(event_data->sp, &event_data->reader); - kfree(event_data); - return 0; -} - -static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_event_data *event_data = file->private_data; - struct event_reader *reader = &event_data->reader; - struct service_processor *sp = event_data->sp; - int ret; - unsigned long flags; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > IBMASM_EVENT_MAX_SIZE) - return 0; - if (*offset != 0) - return 0; - - spin_lock_irqsave(&sp->lock, flags); - if (event_data->active) { - spin_unlock_irqrestore(&sp->lock, flags); - return -EBUSY; - } - event_data->active = 1; - spin_unlock_irqrestore(&sp->lock, flags); - - ret = ibmasm_get_next_event(sp, reader); - if (ret <= 0) - goto out; - - if (count < reader->data_size) { - ret = -EINVAL; - goto out; - } - - if (copy_to_user(buf, reader->data, reader->data_size)) { - ret = -EFAULT; - goto out; - } - ret = reader->data_size; - -out: - event_data->active = 0; - return ret; -} - -static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_event_data *event_data = file->private_data; - - if (*offset < 0) - return -EINVAL; - if (count != 1) - return 0; - if (*offset != 0) - return 0; - - ibmasm_cancel_next_event(&event_data->reader); - return 0; -} - -static int r_heartbeat_file_open(struct inode *inode, struct file *file) -{ - struct ibmasmfs_heartbeat_data *rhbeat; - - if (!inode->i_private) - return -ENODEV; - - rhbeat = kmalloc_obj(struct ibmasmfs_heartbeat_data); - if (!rhbeat) - return -ENOMEM; - - rhbeat->sp = inode->i_private; - rhbeat->active = 0; - ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); - file->private_data = rhbeat; - return 0; -} - -static int r_heartbeat_file_close(struct inode *inode, struct file *file) -{ - struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; - - kfree(rhbeat); - return 0; -} - -static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; - unsigned long flags; - int result; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > 1024) - return 0; - if (*offset != 0) - return 0; - - /* allow only one reverse heartbeat per process */ - spin_lock_irqsave(&rhbeat->sp->lock, flags); - if (rhbeat->active) { - spin_unlock_irqrestore(&rhbeat->sp->lock, flags); - return -EBUSY; - } - rhbeat->active = 1; - spin_unlock_irqrestore(&rhbeat->sp->lock, flags); - - result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); - rhbeat->active = 0; - - return result; -} - -static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; - - if (*offset < 0) - return -EINVAL; - if (count != 1) - return 0; - if (*offset != 0) - return 0; - - if (rhbeat->active) - ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat); - - return 1; -} - -static int remote_settings_file_close(struct inode *inode, struct file *file) -{ - return 0; -} - -static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - void __iomem *address = (void __iomem *)file->private_data; - int len = 0; - unsigned int value; - char lbuf[20]; - - value = readl(address); - len = snprintf(lbuf, sizeof(lbuf), "%d\n", value); - - return simple_read_from_buffer(buf, count, offset, lbuf, len); -} - -static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) -{ - void __iomem *address = (void __iomem *)file->private_data; - char *buff; - unsigned int value; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > 1024) - return 0; - if (*offset != 0) - return 0; - - buff = memdup_user_nul(ubuff, count); - if (IS_ERR(buff)) - return PTR_ERR(buff); - - value = simple_strtoul(buff, NULL, 10); - writel(value, address); - kfree(buff); - - return count; -} - -static const struct file_operations command_fops = { - .open = command_file_open, - .release = command_file_close, - .read = command_file_read, - .write = command_file_write, - .llseek = generic_file_llseek, -}; - -static const struct file_operations event_fops = { - .open = event_file_open, - .release = event_file_close, - .read = event_file_read, - .write = event_file_write, - .llseek = generic_file_llseek, -}; - -static const struct file_operations r_heartbeat_fops = { - .open = r_heartbeat_file_open, - .release = r_heartbeat_file_close, - .read = r_heartbeat_file_read, - .write = r_heartbeat_file_write, - .llseek = generic_file_llseek, -}; - -static const struct file_operations remote_settings_fops = { - .open = simple_open, - .release = remote_settings_file_close, - .read = remote_settings_file_read, - .write = remote_settings_file_write, - .llseek = generic_file_llseek, -}; - - -static void ibmasmfs_create_files (struct super_block *sb) -{ - struct list_head *entry; - struct service_processor *sp; - - list_for_each(entry, &service_processors) { - struct dentry *dir; - struct dentry *remote_dir; - sp = list_entry(entry, struct service_processor, node); - dir = ibmasmfs_create_dir(sb->s_root, sp->dirname); - if (!dir) - continue; - - ibmasmfs_create_file(dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR); - ibmasmfs_create_file(dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR); - ibmasmfs_create_file(dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR); - - remote_dir = ibmasmfs_create_dir(dir, "remote_video"); - if (!remote_dir) - continue; - - ibmasmfs_create_file(remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR); - ibmasmfs_create_file(remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR); - ibmasmfs_create_file(remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR); - } -} diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c deleted file mode 100644 index 5313230f36ad..000000000000 --- a/drivers/misc/ibmasm/lowlevel.c +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#include "ibmasm.h" -#include "lowlevel.h" -#include "i2o.h" -#include "dot_command.h" -#include "remote.h" - -static struct i2o_header header = I2O_HEADER_TEMPLATE; - - -int ibmasm_send_i2o_message(struct service_processor *sp) -{ - u32 mfa; - size_t command_size; - struct i2o_message *message; - struct command *command = sp->current_command; - - command_size = get_dot_command_size(command->buffer); - if (command_size > command->buffer_size) - return 1; - if (command_size > I2O_COMMAND_SIZE) - command_size = I2O_COMMAND_SIZE; - - mfa = get_mfa_inbound(sp->base_address); - if (!mfa) - return 1; - - header.message_size = outgoing_message_size((unsigned int)command_size); - message = get_i2o_message(sp->base_address, mfa); - - memcpy_toio(&message->header, &header, sizeof(struct i2o_header)); - memcpy_toio(&message->data, command->buffer, command_size); - - set_mfa_inbound(sp->base_address, mfa); - - return 0; -} - -irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id) -{ - u32 mfa; - struct service_processor *sp = (struct service_processor *)dev_id; - void __iomem *base_address = sp->base_address; - char tsbuf[32]; - - if (!sp_interrupt_pending(base_address)) - return IRQ_NONE; - - dbg("respond to interrupt at %s\n", get_timestamp(tsbuf)); - - if (mouse_interrupt_pending(sp)) { - ibmasm_handle_mouse_interrupt(sp); - clear_mouse_interrupt(sp); - } - - mfa = get_mfa_outbound(base_address); - if (valid_mfa(mfa)) { - struct i2o_message *msg = get_i2o_message(base_address, mfa); - ibmasm_receive_message(sp, &msg->data, incoming_data_size(msg)); - } else - dbg("didn't get a valid MFA\n"); - - set_mfa_outbound(base_address, mfa); - dbg("finished interrupt at %s\n", get_timestamp(tsbuf)); - - return IRQ_HANDLED; -} diff --git a/drivers/misc/ibmasm/lowlevel.h b/drivers/misc/ibmasm/lowlevel.h deleted file mode 100644 index 25f1ed07c3c5..000000000000 --- a/drivers/misc/ibmasm/lowlevel.h +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -/* Condor service processor specific hardware definitions */ - -#ifndef __IBMASM_CONDOR_H__ -#define __IBMASM_CONDOR_H__ - -#include <asm/io.h> - -#define VENDORID_IBM 0x1014 -#define DEVICEID_RSA 0x010F - -#define GET_MFA_ADDR(x) (x & 0xFFFFFF00) - -#define MAILBOX_FULL(x) (x & 0x00000001) - -#define NO_MFAS_AVAILABLE 0xFFFFFFFF - - -#define INBOUND_QUEUE_PORT 0x40 /* contains address of next free MFA */ -#define OUTBOUND_QUEUE_PORT 0x44 /* contains address of posted MFA */ - -#define SP_INTR_MASK 0x00000008 -#define UART_INTR_MASK 0x00000010 - -#define INTR_STATUS_REGISTER 0x13A0 -#define INTR_CONTROL_REGISTER 0x13A4 - -#define SCOUT_COM_A_BASE 0x0000 -#define SCOUT_COM_B_BASE 0x0100 -#define SCOUT_COM_C_BASE 0x0200 -#define SCOUT_COM_D_BASE 0x0300 - -static inline int sp_interrupt_pending(void __iomem *base_address) -{ - return SP_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER); -} - -static inline int uart_interrupt_pending(void __iomem *base_address) -{ - return UART_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER); -} - -static inline void ibmasm_enable_interrupts(void __iomem *base_address, int mask) -{ - void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER; - writel( readl(ctrl_reg) & ~mask, ctrl_reg); -} - -static inline void ibmasm_disable_interrupts(void __iomem *base_address, int mask) -{ - void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER; - writel( readl(ctrl_reg) | mask, ctrl_reg); -} - -static inline void enable_sp_interrupts(void __iomem *base_address) -{ - ibmasm_enable_interrupts(base_address, SP_INTR_MASK); -} - -static inline void disable_sp_interrupts(void __iomem *base_address) -{ - ibmasm_disable_interrupts(base_address, SP_INTR_MASK); -} - -static inline void enable_uart_interrupts(void __iomem *base_address) -{ - ibmasm_enable_interrupts(base_address, UART_INTR_MASK); -} - -static inline void disable_uart_interrupts(void __iomem *base_address) -{ - ibmasm_disable_interrupts(base_address, UART_INTR_MASK); -} - -#define valid_mfa(mfa) ( (mfa) != NO_MFAS_AVAILABLE ) - -static inline u32 get_mfa_outbound(void __iomem *base_address) -{ - int retry; - u32 mfa; - - for (retry=0; retry<=10; retry++) { - mfa = readl(base_address + OUTBOUND_QUEUE_PORT); - if (valid_mfa(mfa)) - break; - } - return mfa; -} - -static inline void set_mfa_outbound(void __iomem *base_address, u32 mfa) -{ - writel(mfa, base_address + OUTBOUND_QUEUE_PORT); -} - -static inline u32 get_mfa_inbound(void __iomem *base_address) -{ - u32 mfa = readl(base_address + INBOUND_QUEUE_PORT); - - if (MAILBOX_FULL(mfa)) - return 0; - - return mfa; -} - -static inline void set_mfa_inbound(void __iomem *base_address, u32 mfa) -{ - writel(mfa, base_address + INBOUND_QUEUE_PORT); -} - -static inline struct i2o_message *get_i2o_message(void __iomem *base_address, u32 mfa) -{ - return (struct i2o_message *)(GET_MFA_ADDR(mfa) + base_address); -} - -#endif /* __IBMASM_CONDOR_H__ */ diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c deleted file mode 100644 index 4509c15a76a8..000000000000 --- a/drivers/misc/ibmasm/module.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - * - * This driver is based on code originally written by Pete Reynolds - * and others. - */ - -/* - * The ASM device driver does the following things: - * - * 1) When loaded it sends a message to the service processor, - * indicating that an OS is * running. This causes the service processor - * to send periodic heartbeats to the OS. - * - * 2) Answers the periodic heartbeats sent by the service processor. - * Failure to do so would result in system reboot. - * - * 3) Acts as a pass through for dot commands sent from user applications. - * The interface for this is the ibmasmfs file system. - * - * 4) Allows user applications to register for event notification. Events - * are sent to the driver through interrupts. They can be read from user - * space through the ibmasmfs file system. - * - * 5) Allows user space applications to send heartbeats to the service - * processor (aka reverse heartbeats). Again this happens through ibmasmfs. - * - * 6) Handles remote mouse and keyboard event interrupts and makes them - * available to user applications through ibmasmfs. - * - */ - -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/slab.h> -#include "ibmasm.h" -#include "lowlevel.h" -#include "remote.h" - -int ibmasm_debug = 0; -module_param(ibmasm_debug, int , S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(ibmasm_debug, " Set debug mode on or off"); - - -static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id) -{ - int result; - struct service_processor *sp; - - if ((result = pci_enable_device(pdev))) { - dev_err(&pdev->dev, "Failed to enable PCI device\n"); - return result; - } - if ((result = pci_request_regions(pdev, DRIVER_NAME))) { - dev_err(&pdev->dev, "Failed to allocate PCI resources\n"); - goto error_resources; - } - /* vnc client won't work without bus-mastering */ - pci_set_master(pdev); - - sp = kzalloc_obj(struct service_processor); - if (sp == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory\n"); - result = -ENOMEM; - goto error_kmalloc; - } - - spin_lock_init(&sp->lock); - INIT_LIST_HEAD(&sp->command_queue); - - pci_set_drvdata(pdev, (void *)sp); - sp->dev = &pdev->dev; - sp->number = pdev->bus->number; - snprintf(sp->dirname, IBMASM_NAME_SIZE, "%d", sp->number); - snprintf(sp->devname, IBMASM_NAME_SIZE, "%s%d", DRIVER_NAME, sp->number); - - result = ibmasm_event_buffer_init(sp); - if (result) { - dev_err(sp->dev, "Failed to allocate event buffer\n"); - goto error_eventbuffer; - } - - result = ibmasm_heartbeat_init(sp); - if (result) { - dev_err(sp->dev, "Failed to allocate heartbeat command\n"); - goto error_heartbeat; - } - - sp->irq = pdev->irq; - sp->base_address = pci_ioremap_bar(pdev, 0); - if (!sp->base_address) { - dev_err(sp->dev, "Failed to ioremap pci memory\n"); - result = -ENODEV; - goto error_ioremap; - } - - result = request_irq(sp->irq, ibmasm_interrupt_handler, IRQF_SHARED, sp->devname, (void*)sp); - if (result) { - dev_err(sp->dev, "Failed to register interrupt handler\n"); - goto error_request_irq; - } - - enable_sp_interrupts(sp->base_address); - - result = ibmasm_init_remote_input_dev(sp); - if (result) { - dev_err(sp->dev, "Failed to initialize remote queue\n"); - goto error_init_remote; - } - - result = ibmasm_send_driver_vpd(sp); - if (result) { - dev_err(sp->dev, "Failed to send driver VPD to service processor\n"); - goto error_send_message; - } - result = ibmasm_send_os_state(sp, SYSTEM_STATE_OS_UP); - if (result) { - dev_err(sp->dev, "Failed to send OS state to service processor\n"); - goto error_send_message; - } - ibmasmfs_add_sp(sp); - - ibmasm_register_uart(sp); - - return 0; - -error_send_message: - ibmasm_free_remote_input_dev(sp); -error_init_remote: - disable_sp_interrupts(sp->base_address); - free_irq(sp->irq, (void *)sp); -error_request_irq: - iounmap(sp->base_address); -error_ioremap: - ibmasm_heartbeat_exit(sp); -error_heartbeat: - ibmasm_event_buffer_exit(sp); -error_eventbuffer: - kfree(sp); -error_kmalloc: - pci_release_regions(pdev); -error_resources: - pci_disable_device(pdev); - - return result; -} - -static void ibmasm_remove_one(struct pci_dev *pdev) -{ - struct service_processor *sp = pci_get_drvdata(pdev); - - dbg("Unregistering UART\n"); - ibmasm_unregister_uart(sp); - dbg("Sending OS down message\n"); - if (ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN)) - err("failed to get response to 'Send OS State' command\n"); - dbg("Disabling heartbeats\n"); - ibmasm_heartbeat_exit(sp); - dbg("Disabling interrupts\n"); - disable_sp_interrupts(sp->base_address); - dbg("Freeing SP irq\n"); - free_irq(sp->irq, (void *)sp); - dbg("Cleaning up\n"); - ibmasm_free_remote_input_dev(sp); - iounmap(sp->base_address); - ibmasm_event_buffer_exit(sp); - kfree(sp); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static struct pci_device_id ibmasm_pci_table[] = -{ - { PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) }, - {}, -}; - -static struct pci_driver ibmasm_driver = { - .name = DRIVER_NAME, - .id_table = ibmasm_pci_table, - .probe = ibmasm_init_one, - .remove = ibmasm_remove_one, -}; - -static void __exit ibmasm_exit (void) -{ - ibmasm_unregister_panic_notifier(); - ibmasmfs_unregister(); - pci_unregister_driver(&ibmasm_driver); - info(DRIVER_DESC " version " DRIVER_VERSION " unloaded"); -} - -static int __init ibmasm_init(void) -{ - int result = pci_register_driver(&ibmasm_driver); - if (result) - return result; - - result = ibmasmfs_register(); - if (result) { - pci_unregister_driver(&ibmasm_driver); - err("Failed to register ibmasmfs file system"); - return result; - } - - ibmasm_register_panic_notifier(); - info(DRIVER_DESC " version " DRIVER_VERSION " loaded"); - return 0; -} - -module_init(ibmasm_init); -module_exit(ibmasm_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, ibmasm_pci_table); - diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c deleted file mode 100644 index 21c9b6a6f2c3..000000000000 --- a/drivers/misc/ibmasm/r_heartbeat.c +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#include <linux/sched/signal.h> -#include "ibmasm.h" -#include "dot_command.h" - -/* - * Reverse Heartbeat, i.e. heartbeats sent from the driver to the - * service processor. - * These heartbeats are initiated by user level programs. - */ - -/* the reverse heartbeat dot command */ -#pragma pack(1) -static struct { - struct dot_command_header header; - unsigned char command[3]; -} rhb_dot_cmd = { - .header = { - .type = sp_read, - .command_size = 3, - .data_size = 0, - .status = 0 - }, - .command = { 4, 3, 6 } -}; -#pragma pack() - -void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb) -{ - init_waitqueue_head(&rhb->wait); - rhb->stopped = 0; -} - -/* - * start_reverse_heartbeat - * Loop forever, sending a reverse heartbeat dot command to the service - * processor, then sleeping. The loop comes to an end if the service - * processor fails to respond 3 times or we were interrupted. - */ -int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb) -{ - struct command *cmd; - int times_failed = 0; - int result = 1; - - cmd = ibmasm_new_command(sp, sizeof rhb_dot_cmd); - if (!cmd) - return -ENOMEM; - - while (times_failed < 3) { - memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof rhb_dot_cmd); - cmd->status = IBMASM_CMD_PENDING; - ibmasm_exec_command(sp, cmd); - ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL); - - if (cmd->status != IBMASM_CMD_COMPLETE) - times_failed++; - - wait_event_interruptible_timeout(rhb->wait, - rhb->stopped, - REVERSE_HEARTBEAT_TIMEOUT * HZ); - - if (signal_pending(current) || rhb->stopped) { - result = -EINTR; - break; - } - } - command_put(cmd); - rhb->stopped = 0; - - return result; -} - -void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb) -{ - rhb->stopped = 1; - wake_up_interruptible(&rhb->wait); -} diff --git a/drivers/misc/ibmasm/remote.c b/drivers/misc/ibmasm/remote.c deleted file mode 100644 index 521531738c9a..000000000000 --- a/drivers/misc/ibmasm/remote.c +++ /dev/null @@ -1,273 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Authors: Max Asböck <amax@us.ibm.com> - * Vernon Mauery <vernux@us.ibm.com> - */ - -/* Remote mouse and keyboard event handling functions */ - -#include <linux/pci.h> -#include "ibmasm.h" -#include "remote.h" - -#define MOUSE_X_MAX 1600 -#define MOUSE_Y_MAX 1200 - -static const unsigned short xlate_high[XLATE_SIZE] = { - [KEY_SYM_ENTER & 0xff] = KEY_ENTER, - [KEY_SYM_KPSLASH & 0xff] = KEY_KPSLASH, - [KEY_SYM_KPSTAR & 0xff] = KEY_KPASTERISK, - [KEY_SYM_KPMINUS & 0xff] = KEY_KPMINUS, - [KEY_SYM_KPDOT & 0xff] = KEY_KPDOT, - [KEY_SYM_KPPLUS & 0xff] = KEY_KPPLUS, - [KEY_SYM_KP0 & 0xff] = KEY_KP0, - [KEY_SYM_KP1 & 0xff] = KEY_KP1, - [KEY_SYM_KP2 & 0xff] = KEY_KP2, [KEY_SYM_KPDOWN & 0xff] = KEY_KP2, - [KEY_SYM_KP3 & 0xff] = KEY_KP3, - [KEY_SYM_KP4 & 0xff] = KEY_KP4, [KEY_SYM_KPLEFT & 0xff] = KEY_KP4, - [KEY_SYM_KP5 & 0xff] = KEY_KP5, - [KEY_SYM_KP6 & 0xff] = KEY_KP6, [KEY_SYM_KPRIGHT & 0xff] = KEY_KP6, - [KEY_SYM_KP7 & 0xff] = KEY_KP7, - [KEY_SYM_KP8 & 0xff] = KEY_KP8, [KEY_SYM_KPUP & 0xff] = KEY_KP8, - [KEY_SYM_KP9 & 0xff] = KEY_KP9, - [KEY_SYM_BK_SPC & 0xff] = KEY_BACKSPACE, - [KEY_SYM_TAB & 0xff] = KEY_TAB, - [KEY_SYM_CTRL & 0xff] = KEY_LEFTCTRL, - [KEY_SYM_ALT & 0xff] = KEY_LEFTALT, - [KEY_SYM_INSERT & 0xff] = KEY_INSERT, - [KEY_SYM_DELETE & 0xff] = KEY_DELETE, - [KEY_SYM_SHIFT & 0xff] = KEY_LEFTSHIFT, - [KEY_SYM_UARROW & 0xff] = KEY_UP, - [KEY_SYM_DARROW & 0xff] = KEY_DOWN, - [KEY_SYM_LARROW & 0xff] = KEY_LEFT, - [KEY_SYM_RARROW & 0xff] = KEY_RIGHT, - [KEY_SYM_ESCAPE & 0xff] = KEY_ESC, - [KEY_SYM_PAGEUP & 0xff] = KEY_PAGEUP, - [KEY_SYM_PAGEDOWN & 0xff] = KEY_PAGEDOWN, - [KEY_SYM_HOME & 0xff] = KEY_HOME, - [KEY_SYM_END & 0xff] = KEY_END, - [KEY_SYM_F1 & 0xff] = KEY_F1, - [KEY_SYM_F2 & 0xff] = KEY_F2, - [KEY_SYM_F3 & 0xff] = KEY_F3, - [KEY_SYM_F4 & 0xff] = KEY_F4, - [KEY_SYM_F5 & 0xff] = KEY_F5, - [KEY_SYM_F6 & 0xff] = KEY_F6, - [KEY_SYM_F7 & 0xff] = KEY_F7, - [KEY_SYM_F8 & 0xff] = KEY_F8, - [KEY_SYM_F9 & 0xff] = KEY_F9, - [KEY_SYM_F10 & 0xff] = KEY_F10, - [KEY_SYM_F11 & 0xff] = KEY_F11, - [KEY_SYM_F12 & 0xff] = KEY_F12, - [KEY_SYM_CAP_LOCK & 0xff] = KEY_CAPSLOCK, - [KEY_SYM_NUM_LOCK & 0xff] = KEY_NUMLOCK, - [KEY_SYM_SCR_LOCK & 0xff] = KEY_SCROLLLOCK, -}; - -static const unsigned short xlate[XLATE_SIZE] = { - [NO_KEYCODE] = KEY_RESERVED, - [KEY_SYM_SPACE] = KEY_SPACE, - [KEY_SYM_TILDE] = KEY_GRAVE, [KEY_SYM_BKTIC] = KEY_GRAVE, - [KEY_SYM_ONE] = KEY_1, [KEY_SYM_BANG] = KEY_1, - [KEY_SYM_TWO] = KEY_2, [KEY_SYM_AT] = KEY_2, - [KEY_SYM_THREE] = KEY_3, [KEY_SYM_POUND] = KEY_3, - [KEY_SYM_FOUR] = KEY_4, [KEY_SYM_DOLLAR] = KEY_4, - [KEY_SYM_FIVE] = KEY_5, [KEY_SYM_PERCENT] = KEY_5, - [KEY_SYM_SIX] = KEY_6, [KEY_SYM_CARAT] = KEY_6, - [KEY_SYM_SEVEN] = KEY_7, [KEY_SYM_AMPER] = KEY_7, - [KEY_SYM_EIGHT] = KEY_8, [KEY_SYM_STAR] = KEY_8, - [KEY_SYM_NINE] = KEY_9, [KEY_SYM_LPAREN] = KEY_9, - [KEY_SYM_ZERO] = KEY_0, [KEY_SYM_RPAREN] = KEY_0, - [KEY_SYM_MINUS] = KEY_MINUS, [KEY_SYM_USCORE] = KEY_MINUS, - [KEY_SYM_EQUAL] = KEY_EQUAL, [KEY_SYM_PLUS] = KEY_EQUAL, - [KEY_SYM_LBRKT] = KEY_LEFTBRACE, [KEY_SYM_LCURLY] = KEY_LEFTBRACE, - [KEY_SYM_RBRKT] = KEY_RIGHTBRACE, [KEY_SYM_RCURLY] = KEY_RIGHTBRACE, - [KEY_SYM_SLASH] = KEY_BACKSLASH, [KEY_SYM_PIPE] = KEY_BACKSLASH, - [KEY_SYM_TIC] = KEY_APOSTROPHE, [KEY_SYM_QUOTE] = KEY_APOSTROPHE, - [KEY_SYM_SEMIC] = KEY_SEMICOLON, [KEY_SYM_COLON] = KEY_SEMICOLON, - [KEY_SYM_COMMA] = KEY_COMMA, [KEY_SYM_LT] = KEY_COMMA, - [KEY_SYM_PERIOD] = KEY_DOT, [KEY_SYM_GT] = KEY_DOT, - [KEY_SYM_BSLASH] = KEY_SLASH, [KEY_SYM_QMARK] = KEY_SLASH, - [KEY_SYM_A] = KEY_A, [KEY_SYM_a] = KEY_A, - [KEY_SYM_B] = KEY_B, [KEY_SYM_b] = KEY_B, - [KEY_SYM_C] = KEY_C, [KEY_SYM_c] = KEY_C, - [KEY_SYM_D] = KEY_D, [KEY_SYM_d] = KEY_D, - [KEY_SYM_E] = KEY_E, [KEY_SYM_e] = KEY_E, - [KEY_SYM_F] = KEY_F, [KEY_SYM_f] = KEY_F, - [KEY_SYM_G] = KEY_G, [KEY_SYM_g] = KEY_G, - [KEY_SYM_H] = KEY_H, [KEY_SYM_h] = KEY_H, - [KEY_SYM_I] = KEY_I, [KEY_SYM_i] = KEY_I, - [KEY_SYM_J] = KEY_J, [KEY_SYM_j] = KEY_J, - [KEY_SYM_K] = KEY_K, [KEY_SYM_k] = KEY_K, - [KEY_SYM_L] = KEY_L, [KEY_SYM_l] = KEY_L, - [KEY_SYM_M] = KEY_M, [KEY_SYM_m] = KEY_M, - [KEY_SYM_N] = KEY_N, [KEY_SYM_n] = KEY_N, - [KEY_SYM_O] = KEY_O, [KEY_SYM_o] = KEY_O, - [KEY_SYM_P] = KEY_P, [KEY_SYM_p] = KEY_P, - [KEY_SYM_Q] = KEY_Q, [KEY_SYM_q] = KEY_Q, - [KEY_SYM_R] = KEY_R, [KEY_SYM_r] = KEY_R, - [KEY_SYM_S] = KEY_S, [KEY_SYM_s] = KEY_S, - [KEY_SYM_T] = KEY_T, [KEY_SYM_t] = KEY_T, - [KEY_SYM_U] = KEY_U, [KEY_SYM_u] = KEY_U, - [KEY_SYM_V] = KEY_V, [KEY_SYM_v] = KEY_V, - [KEY_SYM_W] = KEY_W, [KEY_SYM_w] = KEY_W, - [KEY_SYM_X] = KEY_X, [KEY_SYM_x] = KEY_X, - [KEY_SYM_Y] = KEY_Y, [KEY_SYM_y] = KEY_Y, - [KEY_SYM_Z] = KEY_Z, [KEY_SYM_z] = KEY_Z, -}; - -static void print_input(struct remote_input *input) -{ - if (input->type == INPUT_TYPE_MOUSE) { - unsigned char buttons = input->mouse_buttons; - dbg("remote mouse movement: (x,y)=(%d,%d)%s%s%s%s\n", - input->data.mouse.x, input->data.mouse.y, - (buttons) ? " -- buttons:" : "", - (buttons & REMOTE_BUTTON_LEFT) ? "left " : "", - (buttons & REMOTE_BUTTON_MIDDLE) ? "middle " : "", - (buttons & REMOTE_BUTTON_RIGHT) ? "right" : "" - ); - } else { - dbg("remote keypress (code, flag, down):" - "%d (0x%x) [0x%x] [0x%x]\n", - input->data.keyboard.key_code, - input->data.keyboard.key_code, - input->data.keyboard.key_flag, - input->data.keyboard.key_down - ); - } -} - -static void send_mouse_event(struct input_dev *dev, struct remote_input *input) -{ - unsigned char buttons = input->mouse_buttons; - - input_report_abs(dev, ABS_X, input->data.mouse.x); - input_report_abs(dev, ABS_Y, input->data.mouse.y); - input_report_key(dev, BTN_LEFT, buttons & REMOTE_BUTTON_LEFT); - input_report_key(dev, BTN_MIDDLE, buttons & REMOTE_BUTTON_MIDDLE); - input_report_key(dev, BTN_RIGHT, buttons & REMOTE_BUTTON_RIGHT); - input_sync(dev); -} - -static void send_keyboard_event(struct input_dev *dev, - struct remote_input *input) -{ - unsigned int key; - unsigned short code = input->data.keyboard.key_code; - - if (code & 0xff00) - key = xlate_high[code & 0xff]; - else - key = xlate[code]; - input_report_key(dev, key, input->data.keyboard.key_down); - input_sync(dev); -} - -void ibmasm_handle_mouse_interrupt(struct service_processor *sp) -{ - unsigned long reader; - unsigned long writer; - struct remote_input input; - - reader = get_queue_reader(sp); - writer = get_queue_writer(sp); - - while (reader != writer) { - if (reader >= REMOTE_QUEUE_SIZE || writer >= REMOTE_QUEUE_SIZE) { - set_queue_reader(sp, 0); - break; - } - - memcpy_fromio(&input, get_queue_entry(sp, reader), - sizeof(struct remote_input)); - - print_input(&input); - if (input.type == INPUT_TYPE_MOUSE) { - send_mouse_event(sp->remote.mouse_dev, &input); - } else if (input.type == INPUT_TYPE_KEYBOARD) { - send_keyboard_event(sp->remote.keybd_dev, &input); - } else - break; - - reader = advance_queue_reader(sp, reader); - writer = get_queue_writer(sp); - } -} - -int ibmasm_init_remote_input_dev(struct service_processor *sp) -{ - /* set up the mouse input device */ - struct input_dev *mouse_dev, *keybd_dev; - struct pci_dev *pdev = to_pci_dev(sp->dev); - int error = -ENOMEM; - int i; - - sp->remote.mouse_dev = mouse_dev = input_allocate_device(); - sp->remote.keybd_dev = keybd_dev = input_allocate_device(); - - if (!mouse_dev || !keybd_dev) - goto err_free_devices; - - mouse_dev->id.bustype = BUS_PCI; - mouse_dev->id.vendor = pdev->vendor; - mouse_dev->id.product = pdev->device; - mouse_dev->id.version = 1; - mouse_dev->dev.parent = sp->dev; - mouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - mouse_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); - set_bit(BTN_TOUCH, mouse_dev->keybit); - mouse_dev->name = "ibmasm RSA I remote mouse"; - input_set_abs_params(mouse_dev, ABS_X, 0, MOUSE_X_MAX, 0, 0); - input_set_abs_params(mouse_dev, ABS_Y, 0, MOUSE_Y_MAX, 0, 0); - - keybd_dev->id.bustype = BUS_PCI; - keybd_dev->id.vendor = pdev->vendor; - keybd_dev->id.product = pdev->device; - keybd_dev->id.version = 2; - keybd_dev->dev.parent = sp->dev; - keybd_dev->evbit[0] = BIT_MASK(EV_KEY); - keybd_dev->name = "ibmasm RSA I remote keyboard"; - - for (i = 0; i < XLATE_SIZE; i++) { - if (xlate_high[i]) - set_bit(xlate_high[i], keybd_dev->keybit); - if (xlate[i]) - set_bit(xlate[i], keybd_dev->keybit); - } - - error = input_register_device(mouse_dev); - if (error) - goto err_free_devices; - - error = input_register_device(keybd_dev); - if (error) - goto err_unregister_mouse_dev; - - enable_mouse_interrupts(sp); - - printk(KERN_INFO "ibmasm remote responding to events on RSA card %d\n", sp->number); - - return 0; - - err_unregister_mouse_dev: - input_unregister_device(mouse_dev); - mouse_dev = NULL; /* so we don't try to free it again below */ - err_free_devices: - input_free_device(mouse_dev); - input_free_device(keybd_dev); - - return error; -} - -void ibmasm_free_remote_input_dev(struct service_processor *sp) -{ - disable_mouse_interrupts(sp); - input_unregister_device(sp->remote.mouse_dev); - input_unregister_device(sp->remote.keybd_dev); -} - diff --git a/drivers/misc/ibmasm/remote.h b/drivers/misc/ibmasm/remote.h deleted file mode 100644 index ec4e78ec5a67..000000000000 --- a/drivers/misc/ibmasm/remote.h +++ /dev/null @@ -1,257 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - * - * Originally written by Pete Reynolds - */ - -#ifndef _IBMASM_REMOTE_H_ -#define _IBMASM_REMOTE_H_ - -#include <asm/io.h> - -/* pci offsets */ -#define CONDOR_MOUSE_DATA 0x000AC000 -#define CONDOR_MOUSE_ISR_CONTROL 0x00 -#define CONDOR_MOUSE_ISR_STATUS 0x04 -#define CONDOR_MOUSE_Q_READER 0x08 -#define CONDOR_MOUSE_Q_WRITER 0x0C -#define CONDOR_MOUSE_Q_BEGIN 0x10 -#define CONDOR_MOUSE_MAX_X 0x14 -#define CONDOR_MOUSE_MAX_Y 0x18 - -#define CONDOR_INPUT_DESKTOP_INFO 0x1F0 -#define CONDOR_INPUT_DISPLAY_RESX 0x1F4 -#define CONDOR_INPUT_DISPLAY_RESY 0x1F8 -#define CONDOR_INPUT_DISPLAY_BITS 0x1FC -#define CONDOR_OUTPUT_VNC_STATUS 0x200 - -#define CONDOR_MOUSE_INTR_STATUS_MASK 0x00000001 - -#define INPUT_TYPE_MOUSE 0x1 -#define INPUT_TYPE_KEYBOARD 0x2 - - -/* mouse button states received from SP */ -#define REMOTE_DOUBLE_CLICK 0xF0 -#define REMOTE_BUTTON_LEFT 0x01 -#define REMOTE_BUTTON_MIDDLE 0x02 -#define REMOTE_BUTTON_RIGHT 0x04 - -/* size of keysym/keycode translation matrices */ -#define XLATE_SIZE 256 - -struct mouse_input { - unsigned short y; - unsigned short x; -}; - - -struct keyboard_input { - unsigned short key_code; - unsigned char key_flag; - unsigned char key_down; -}; - - - -struct remote_input { - union { - struct mouse_input mouse; - struct keyboard_input keyboard; - } data; - - unsigned char type; - unsigned char pad1; - unsigned char mouse_buttons; - unsigned char pad3; -}; - -#define mouse_addr(sp) (sp->base_address + CONDOR_MOUSE_DATA) -#define display_width(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESX) -#define display_height(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESY) -#define display_depth(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_BITS) -#define desktop_info(sp) (mouse_addr(sp) + CONDOR_INPUT_DESKTOP_INFO) -#define vnc_status(sp) (mouse_addr(sp) + CONDOR_OUTPUT_VNC_STATUS) -#define isr_control(sp) (mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) - -#define mouse_interrupt_pending(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS) -#define clear_mouse_interrupt(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS) -#define enable_mouse_interrupts(sp) writel(1, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) -#define disable_mouse_interrupts(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) - -/* remote input queue operations */ -#define REMOTE_QUEUE_SIZE 60 - -#define get_queue_writer(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_WRITER) -#define get_queue_reader(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_READER) -#define set_queue_reader(sp, reader) writel(reader, mouse_addr(sp) + CONDOR_MOUSE_Q_READER) - -#define queue_begin (mouse_addr(sp) + CONDOR_MOUSE_Q_BEGIN) - -#define get_queue_entry(sp, read_index) \ - ((void*)(queue_begin + read_index * sizeof(struct remote_input))) - -static inline int advance_queue_reader(struct service_processor *sp, unsigned long reader) -{ - reader++; - if (reader == REMOTE_QUEUE_SIZE) - reader = 0; - - set_queue_reader(sp, reader); - return reader; -} - -#define NO_KEYCODE 0 -#define KEY_SYM_BK_SPC 0xFF08 -#define KEY_SYM_TAB 0xFF09 -#define KEY_SYM_ENTER 0xFF0D -#define KEY_SYM_SCR_LOCK 0xFF14 -#define KEY_SYM_ESCAPE 0xFF1B -#define KEY_SYM_HOME 0xFF50 -#define KEY_SYM_LARROW 0xFF51 -#define KEY_SYM_UARROW 0xFF52 -#define KEY_SYM_RARROW 0xFF53 -#define KEY_SYM_DARROW 0xFF54 -#define KEY_SYM_PAGEUP 0xFF55 -#define KEY_SYM_PAGEDOWN 0xFF56 -#define KEY_SYM_END 0xFF57 -#define KEY_SYM_INSERT 0xFF63 -#define KEY_SYM_NUM_LOCK 0xFF7F -#define KEY_SYM_KPSTAR 0xFFAA -#define KEY_SYM_KPPLUS 0xFFAB -#define KEY_SYM_KPMINUS 0xFFAD -#define KEY_SYM_KPDOT 0xFFAE -#define KEY_SYM_KPSLASH 0xFFAF -#define KEY_SYM_KPRIGHT 0xFF96 -#define KEY_SYM_KPUP 0xFF97 -#define KEY_SYM_KPLEFT 0xFF98 -#define KEY_SYM_KPDOWN 0xFF99 -#define KEY_SYM_KP0 0xFFB0 -#define KEY_SYM_KP1 0xFFB1 -#define KEY_SYM_KP2 0xFFB2 -#define KEY_SYM_KP3 0xFFB3 -#define KEY_SYM_KP4 0xFFB4 -#define KEY_SYM_KP5 0xFFB5 -#define KEY_SYM_KP6 0xFFB6 -#define KEY_SYM_KP7 0xFFB7 -#define KEY_SYM_KP8 0xFFB8 -#define KEY_SYM_KP9 0xFFB9 -#define KEY_SYM_F1 0xFFBE // 1B 5B 5B 41 -#define KEY_SYM_F2 0xFFBF // 1B 5B 5B 42 -#define KEY_SYM_F3 0xFFC0 // 1B 5B 5B 43 -#define KEY_SYM_F4 0xFFC1 // 1B 5B 5B 44 -#define KEY_SYM_F5 0xFFC2 // 1B 5B 5B 45 -#define KEY_SYM_F6 0xFFC3 // 1B 5B 31 37 7E -#define KEY_SYM_F7 0xFFC4 // 1B 5B 31 38 7E -#define KEY_SYM_F8 0xFFC5 // 1B 5B 31 39 7E -#define KEY_SYM_F9 0xFFC6 // 1B 5B 32 30 7E -#define KEY_SYM_F10 0xFFC7 // 1B 5B 32 31 7E -#define KEY_SYM_F11 0xFFC8 // 1B 5B 32 33 7E -#define KEY_SYM_F12 0xFFC9 // 1B 5B 32 34 7E -#define KEY_SYM_SHIFT 0xFFE1 -#define KEY_SYM_CTRL 0xFFE3 -#define KEY_SYM_ALT 0xFFE9 -#define KEY_SYM_CAP_LOCK 0xFFE5 -#define KEY_SYM_DELETE 0xFFFF -#define KEY_SYM_TILDE 0x60 -#define KEY_SYM_BKTIC 0x7E -#define KEY_SYM_ONE 0x31 -#define KEY_SYM_BANG 0x21 -#define KEY_SYM_TWO 0x32 -#define KEY_SYM_AT 0x40 -#define KEY_SYM_THREE 0x33 -#define KEY_SYM_POUND 0x23 -#define KEY_SYM_FOUR 0x34 -#define KEY_SYM_DOLLAR 0x24 -#define KEY_SYM_FIVE 0x35 -#define KEY_SYM_PERCENT 0x25 -#define KEY_SYM_SIX 0x36 -#define KEY_SYM_CARAT 0x5E -#define KEY_SYM_SEVEN 0x37 -#define KEY_SYM_AMPER 0x26 -#define KEY_SYM_EIGHT 0x38 -#define KEY_SYM_STAR 0x2A -#define KEY_SYM_NINE 0x39 -#define KEY_SYM_LPAREN 0x28 -#define KEY_SYM_ZERO 0x30 -#define KEY_SYM_RPAREN 0x29 -#define KEY_SYM_MINUS 0x2D -#define KEY_SYM_USCORE 0x5F -#define KEY_SYM_EQUAL 0x2B -#define KEY_SYM_PLUS 0x3D -#define KEY_SYM_LBRKT 0x5B -#define KEY_SYM_LCURLY 0x7B -#define KEY_SYM_RBRKT 0x5D -#define KEY_SYM_RCURLY 0x7D -#define KEY_SYM_SLASH 0x5C -#define KEY_SYM_PIPE 0x7C -#define KEY_SYM_TIC 0x27 -#define KEY_SYM_QUOTE 0x22 -#define KEY_SYM_SEMIC 0x3B -#define KEY_SYM_COLON 0x3A -#define KEY_SYM_COMMA 0x2C -#define KEY_SYM_LT 0x3C -#define KEY_SYM_PERIOD 0x2E -#define KEY_SYM_GT 0x3E -#define KEY_SYM_BSLASH 0x2F -#define KEY_SYM_QMARK 0x3F -#define KEY_SYM_A 0x41 -#define KEY_SYM_B 0x42 -#define KEY_SYM_C 0x43 -#define KEY_SYM_D 0x44 -#define KEY_SYM_E 0x45 -#define KEY_SYM_F 0x46 -#define KEY_SYM_G 0x47 -#define KEY_SYM_H 0x48 -#define KEY_SYM_I 0x49 -#define KEY_SYM_J 0x4A -#define KEY_SYM_K 0x4B -#define KEY_SYM_L 0x4C -#define KEY_SYM_M 0x4D -#define KEY_SYM_N 0x4E -#define KEY_SYM_O 0x4F -#define KEY_SYM_P 0x50 -#define KEY_SYM_Q 0x51 -#define KEY_SYM_R 0x52 -#define KEY_SYM_S 0x53 -#define KEY_SYM_T 0x54 -#define KEY_SYM_U 0x55 -#define KEY_SYM_V 0x56 -#define KEY_SYM_W 0x57 -#define KEY_SYM_X 0x58 -#define KEY_SYM_Y 0x59 -#define KEY_SYM_Z 0x5A -#define KEY_SYM_a 0x61 -#define KEY_SYM_b 0x62 -#define KEY_SYM_c 0x63 -#define KEY_SYM_d 0x64 -#define KEY_SYM_e 0x65 -#define KEY_SYM_f 0x66 -#define KEY_SYM_g 0x67 -#define KEY_SYM_h 0x68 -#define KEY_SYM_i 0x69 -#define KEY_SYM_j 0x6A -#define KEY_SYM_k 0x6B -#define KEY_SYM_l 0x6C -#define KEY_SYM_m 0x6D -#define KEY_SYM_n 0x6E -#define KEY_SYM_o 0x6F -#define KEY_SYM_p 0x70 -#define KEY_SYM_q 0x71 -#define KEY_SYM_r 0x72 -#define KEY_SYM_s 0x73 -#define KEY_SYM_t 0x74 -#define KEY_SYM_u 0x75 -#define KEY_SYM_v 0x76 -#define KEY_SYM_w 0x77 -#define KEY_SYM_x 0x78 -#define KEY_SYM_y 0x79 -#define KEY_SYM_z 0x7A -#define KEY_SYM_SPACE 0x20 -#endif /* _IBMASM_REMOTE_H_ */ diff --git a/drivers/misc/ibmasm/uart.c b/drivers/misc/ibmasm/uart.c deleted file mode 100644 index a5d4c8e010ee..000000000000 --- a/drivers/misc/ibmasm/uart.c +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck <amax@us.ibm.com> - */ - -#include <linux/termios.h> -#include <linux/tty.h> -#include <linux/serial_core.h> -#include <linux/serial_reg.h> -#include <linux/serial_8250.h> -#include "ibmasm.h" -#include "lowlevel.h" - - -void ibmasm_register_uart(struct service_processor *sp) -{ - struct uart_8250_port uart; - void __iomem *iomem_base; - - iomem_base = sp->base_address + SCOUT_COM_B_BASE; - - /* read the uart scratch register to determine if the UART - * is dedicated to the service processor or if the OS can use it - */ - if (0 == readl(iomem_base + UART_SCR)) { - dev_info(sp->dev, "IBM SP UART not registered, owned by service processor\n"); - sp->serial_line = -1; - return; - } - - memset(&uart, 0, sizeof(uart)); - uart.port.irq = sp->irq; - uart.port.uartclk = 3686400; - uart.port.flags = UPF_SHARE_IRQ; - uart.port.iotype = UPIO_MEM; - uart.port.membase = iomem_base; - - sp->serial_line = serial8250_register_8250_port(&uart); - if (sp->serial_line < 0) { - dev_err(sp->dev, "Failed to register serial port\n"); - return; - } - enable_uart_interrupts(sp->base_address); -} - -void ibmasm_unregister_uart(struct service_processor *sp) -{ - if (sp->serial_line < 0) - return; - - disable_uart_interrupts(sp->base_address); - serial8250_unregister_port(sp->serial_line); -} diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c index beb18c34f20d..da82b1edb7c9 100644 --- a/drivers/misc/ibmvmc.c +++ b/drivers/misc/ibmvmc.c @@ -1040,7 +1040,7 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct inode *inode; - struct ibmvmc_buffer *vmc_buffer; + struct ibmvmc_buffer *vmc_buffer = NULL; struct ibmvmc_file_session *session; struct crq_server_adapter *adapter; struct ibmvmc_hmc *hmc; @@ -1130,9 +1130,15 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer, dev_dbg(adapter->dev, "write: file = 0x%lx, count = 0x%lx\n", (unsigned long)file, (unsigned long)count); - ibmvmc_send_msg(adapter, vmc_buffer, hmc, count); + if (ibmvmc_send_msg(adapter, vmc_buffer, hmc, count)) { + ret = -EIO; + goto out; + } + vmc_buffer = NULL; ret = p - buffer; out: + if (vmc_buffer) + vmc_buffer->free = 1; spin_unlock_irqrestore(&hmc->lock, flags); return (ssize_t)(ret); } @@ -1653,6 +1659,13 @@ static int ibmvmc_recv_msg(struct crq_server_adapter *adapter, return -1; } + if (msg_len > buffer->size) { + dev_err(adapter->dev, "Recv_msg: msg_len 0x%lx exceeds buffer size 0x%x\n", + msg_len, buffer->size); + spin_unlock_irqrestore(&hmc->lock, flags); + return -1; + } + /* RDMA the data into the partition. */ rc = h_copy_rdma(msg_len, adapter->riobn, @@ -2298,8 +2311,8 @@ static void ibmvmc_remove(struct vio_dev *vdev) } static struct vio_device_id ibmvmc_device_table[] = { - { "ibm,vmc", "IBM,vmc" }, - { "", "" } + { .type = "ibm,vmc", .compat = "IBM,vmc" }, + { } }; MODULE_DEVICE_TABLE(vio, ibmvmc_device_table); diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index 015710762a65..e8bcf0df2da8 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c @@ -95,7 +95,7 @@ static int ics932s401_detect(struct i2c_client *client, static void ics932s401_remove(struct i2c_client *client); static const struct i2c_device_id ics932s401_id[] = { - { "ics932s401" }, + { .name = "ics932s401" }, { } }; MODULE_DEVICE_TABLE(i2c, ics932s401_id); diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index 95480e16ae5f..c9b526a212d0 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -449,8 +449,8 @@ static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id isl29003_id[] = { - { "isl29003" }, - {} + { .name = "isl29003" }, + { } }; MODULE_DEVICE_TABLE(i2c, isl29003_id); diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index c288aeec16c0..dd966db28ec4 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -177,7 +177,7 @@ static void isl29020_remove(struct i2c_client *client) } static const struct i2c_device_id isl29020_id[] = { - { "isl29020" }, + { .name = "isl29020" }, { } }; diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig new file mode 100644 index 000000000000..12a3fd809969 --- /dev/null +++ b/drivers/misc/issei/Kconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023-2026 Intel Corporation +config INTEL_SSEI + tristate "Intel Silicon Security Engine Interface" + help + The ISSEI (Intel Silicon Security Engine Interface) + subsystem provides a communication channel between the host and the + Silicon Security Engine. + Enable this driver to get SPDM and other features on Intel client CPUs + released in 2024 (Lunar Lake) or later. + + If selected, the /dev/isseiX device will be created. + If in doubt, select N. + +if INTEL_SSEI + +config INTEL_SSEI_HW_HECI + tristate "Intel Silicon Security Engine Interface Hardware" + depends on X86 && PCI + help + HECI interface of communication channel between + the host and the Silicon Security Engine. + + Implementation of ISSEI communication channel over + the HECI hardware PCI device. + This device is available on Intel client CPUs released in 2024 + (Lunar Lake) or later. + +endif diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile new file mode 100644 index 000000000000..8fac360c3342 --- /dev/null +++ b/drivers/misc/issei/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023-2026 Intel Corporation +ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"INTEL_SSEI"' + +obj-$(CONFIG_INTEL_SSEI) += issei.o +issei-objs += cdev.o +issei-objs += dma.o +issei-objs += fw_client.o +issei-objs += host_client.o +issei-objs += ham.o +issei-objs += main.o + +obj-$(CONFIG_INTEL_SSEI_HW_HECI) += issei-heci.o +issei-heci-objs := pci_heci.o +issei-heci-objs += hw_heci.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c new file mode 100644 index 000000000000..b7d65e2d0813 --- /dev/null +++ b/drivers/misc/issei/cdev.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include <linux/atomic.h> +#include <linux/cdev.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/fs.h> +#include <linux/issei.h> +#include <linux/kobject.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/sched/signal.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/xarray.h> + +#include "issei_dev.h" +#include "host_client.h" +#include "cdev.h" + +struct class *issei_class; +static dev_t issei_devt; + +#define ISSEI_MAX_DEVS MINORMASK + +static DEFINE_XARRAY_ALLOC(issei_minor_xa); + +static int issei_open(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl; + struct issei_device *idev; + + xa_lock(&issei_minor_xa); + idev = xa_load(&issei_minor_xa, iminor(inode)); + if (idev) + get_device(&idev->dev); + xa_unlock(&issei_minor_xa); + if (!idev) + return -ENODEV; + + cl = issei_cl_create(idev, fp); + if (IS_ERR(cl)) { + put_device(&idev->dev); + return PTR_ERR(cl); + } + fp->private_data = cl; + + return nonseekable_open(inode, fp); +} + +static int issei_release(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl = fp->private_data; + struct issei_device *idev = cl->idev; + + issei_cl_remove(cl); + put_device(&idev->dev); + + return 0; +} + +static long issei_ioctl(struct file *file, unsigned int cmd, unsigned long data) +{ + struct issei_host_client *cl = file->private_data; + struct issei_connect_client_data conn; + struct issei_device *idev = cl->idev; + int ret; + + switch (cmd) { + case IOCTL_ISSEI_CONNECT_CLIENT: + dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { + dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return -EFAULT; + } + + ret = issei_cl_connect(cl, (uuid_t *)&conn.in_client_uuid, + &conn.out_client_properties.max_msg_length, + &conn.out_client_properties.protocol_version, + &conn.out_client_properties.flags); + if (ret) + return ret; + + if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { + dev_dbg(&idev->dev, "failed to copy data to userland\n"); + issei_cl_disconnect(cl); + return -EFAULT; + } + return 0; + + case IOCTL_ISSEI_DISCONNECT_CLIENT: + dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + return issei_cl_disconnect(cl); + + default: + return -ENOIOCTLCMD; + } +} + +static ssize_t issei_write(struct file *file, const char __user *ubuf, + size_t length, loff_t *offset) +{ + struct issei_host_client *cl = file->private_data; + struct issei_device *idev = cl->idev; + ssize_t ret; + + if (!length) + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.h2f) { + dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", + length, idev->dma.length.h2f); + return -EFBIG; + } + + u8 *buf __free(kfree) = memdup_user(ubuf, length); + if (IS_ERR(buf)) { + dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return PTR_ERR(buf); + } + + do { + ret = issei_cl_write(cl, buf, length); + if (ret < 0 && ret != -EAGAIN) + return ret; + /* buf is consumed by issei_cl_write on success */ + if (ret >= 0) + retain_and_null_ptr(buf); + if (wait_event_interruptible(cl->write_wait, issei_cl_check_write(cl) != 1)) { + issei_cl_clean_all_wbuf(cl); + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + } while (ret == -EAGAIN); + + return ret; +} + +static ssize_t issei_read(struct file *file, char __user *ubuf, + size_t length, loff_t *offset) +{ + struct issei_host_client *cl = file->private_data; + struct issei_device *idev = cl->idev; + u8 *data = NULL; + ssize_t ret; + + if (!length) + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.f2h) { + dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", + length, idev->dma.length.f2h); + return -EFBIG; + } + + ret = issei_cl_read(cl, &data, length); + if (ret < 0) { + if (ret != -ENOENT) + return ret; + + if (wait_event_interruptible(cl->read_wait, issei_cl_check_read(cl) != 0)) { + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + + ret = issei_cl_read(cl, &data, length); + if (ret < 0) + return ret; + } + + if (copy_to_user(ubuf, data, ret)) { + dev_dbg(&idev->dev, "failed to copy data to userland\n"); + ret = -EFAULT; + } else { + *offset = 0; + } + + kfree(data); + + return ret; +} + +static __poll_t issei_poll(struct file *file, poll_table *wait) +{ + __poll_t req_events = poll_requested_events(wait); + struct issei_host_client *cl = file->private_data; + struct issei_device *idev = cl->idev; + __poll_t mask = 0; + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return EPOLLERR; + } + + if (req_events & (EPOLLIN | EPOLLRDNORM)) { + poll_wait(file, &cl->read_wait, wait); + ret = issei_cl_check_read(cl); + if (ret == 1) + mask |= EPOLLIN | EPOLLRDNORM; + else if (ret < 0) + mask |= EPOLLERR; + } + + if (req_events & (EPOLLOUT | EPOLLWRNORM)) { + poll_wait(file, &cl->write_wait, wait); + ret = issei_cl_check_write(cl); + if (ret == 0) + mask |= EPOLLOUT | EPOLLWRNORM; + else if (ret < 0) + mask |= EPOLLERR; + } + + return mask; +} + +static ssize_t fw_ver_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct issei_device *idev = dev_get_drvdata(device); + + return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_version[1], + idev->fw_version[2], idev->fw_version[3]); +} +static DEVICE_ATTR_RO(fw_ver); + +static struct attribute *issei_attrs[] = { + &dev_attr_fw_ver.attr, + NULL +}; +ATTRIBUTE_GROUPS(issei); + +static const struct file_operations issei_fops = { + .owner = THIS_MODULE, + .open = issei_open, + .unlocked_ioctl = issei_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .write = issei_write, + .read = issei_read, + .release = issei_release, + .poll = issei_poll, +}; + +static void issei_device_release(struct device *dev) +{ + kfree(dev_get_drvdata(dev)); +} + +static void issei_device_init(struct issei_device *idev, struct device *parent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops) +{ + idev->parent = parent; + idev->power_down = false; + init_waitqueue_head(&idev->wait_has_data); + idev->has_data = false; + init_waitqueue_head(&idev->wait_rst_state); + idev->rst_state = ISSEI_RST_STATE_INIT; + + mutex_init(&idev->client_lock); + INIT_LIST_HEAD(&idev->host_client_list); + idev->host_client_last_id = 0; + idev->host_client_count = 0; + INIT_LIST_HEAD(&idev->fw_client_list); + INIT_LIST_HEAD(&idev->write_queue); + idev->last_write_ts = 0; + + idev->dma.length = *dma_length; + + idev->ops = ops; +} + +/** + * issei_register: register issei character device + * @hw_size: size of the hardware structure to allocate + * @parent: parent device + * @dma_length: structure with DMA sizes + * @ops: hardware-related operations + * + * Return: pointer allocated to issei_device structure, error on failure + */ +struct issei_device *issei_register(size_t hw_size, struct device *parent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops) +{ + struct issei_device *idev; + u32 minor; + int ret, devno; + + idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL); + if (!idev) + return ERR_PTR(-ENOMEM); + + issei_device_init(idev, parent, dma_length, ops); + + ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL); + if (ret < 0) { + dev_err(&idev->dev, "Failed to allocate minor. ret = %d\n", ret); + kfree(idev); + return ERR_PTR(ret); + } + + idev->minor = minor; + devno = MKDEV(MAJOR(issei_devt), idev->minor); + + device_initialize(&idev->dev); + idev->dev.devt = devno; + idev->dev.class = issei_class; + idev->dev.parent = parent; + idev->dev.groups = issei_groups; + idev->dev.release = issei_device_release; + dev_set_drvdata(&idev->dev, idev); + + idev->cdev = cdev_alloc(); + if (!idev->cdev) { + ret = -ENOMEM; + goto err; + } + idev->cdev->ops = &issei_fops; + if (parent->driver) + idev->cdev->owner = parent->driver->owner; + cdev_set_parent(idev->cdev, &idev->dev.kobj); + + ret = cdev_add(idev->cdev, devno, 1); + if (ret) { + dev_err(parent, "unable to add device %d:%u ret = %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + ret = dev_set_name(&idev->dev, "issei%u", idev->minor); + if (ret) { + dev_err(parent, "unable to set name to device %d:%u ret = %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + ret = device_add(&idev->dev); + if (ret) { + dev_err(parent, "unable to add device %d:%u ret = %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + idev->fw_clients = kset_create_and_add("fw_clients", NULL, &idev->dev.kobj); + if (!idev->fw_clients) { + ret = -ENOMEM; + goto err_del_dev; + } + + return idev; + +err_del_dev: + device_del(&idev->dev); +err_del_cdev: + cdev_del(idev->cdev); +err: + put_device(&idev->dev); + xa_erase(&issei_minor_xa, minor); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(issei_register); + +/** + * issei_deregister: remove issei character device + * @idev: the device structure + */ +void issei_deregister(struct issei_device *idev) +{ + u32 minor = idev->minor; + + cdev_del(idev->cdev); + + kset_unregister(idev->fw_clients); + + device_del(&idev->dev); + + put_device(&idev->dev); + + xa_erase(&issei_minor_xa, minor); +} +EXPORT_SYMBOL_GPL(issei_deregister); + +static int __init issei_cdev_init(void) +{ + int ret; + + issei_class = class_create("issei"); + if (IS_ERR(issei_class)) { + pr_err("couldn't create class\n"); + return PTR_ERR(issei_class); + } + + ret = alloc_chrdev_region(&issei_devt, 0, ISSEI_MAX_DEVS, "issei"); + if (ret < 0) { + pr_err("unable to allocate char dev region\n"); + class_destroy(issei_class); + return ret; + } + + return 0; +} + +static void __exit issei_cdev_exit(void) +{ + unregister_chrdev_region(issei_devt, ISSEI_MAX_DEVS); + class_destroy(issei_class); +} + +module_init(issei_cdev_init); +module_exit(issei_cdev_exit); + +MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/issei/cdev.h b/drivers/misc/issei/cdev.h new file mode 100644 index 000000000000..30075a624f2d --- /dev/null +++ b/drivers/misc/issei/cdev.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_CDEV_H_ +#define _ISSEI_CDEV_H_ + +struct device; +struct issei_device; +struct issei_dma_length; +struct issei_hw_ops; + +struct issei_device *issei_register(size_t hw_size, struct device *parent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops); +void issei_deregister(struct issei_device *idev); + +#endif /* _ISSEI_CDEV_H_ */ diff --git a/drivers/misc/issei/dma.c b/drivers/misc/issei/dma.c new file mode 100644 index 000000000000..457d28f31c06 --- /dev/null +++ b/drivers/misc/issei/dma.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include <linux/dev_printk.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timekeeping.h> + +#include "issei_dev.h" +#include "hw_msg.h" + +static inline size_t __issei_dma_size(const struct issei_dma *dma) +{ + return dma->length.h2f + dma->length.f2h + dma->length.ctl; +} + +/** + * issei_dmam_setup - setup DMA buffer and clean it + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_dmam_setup(struct issei_device *idev) +{ + struct issei_dma *dma = &idev->dma; + size_t size; + + size = __issei_dma_size(dma); + if (!size) + return -EINVAL; + + if (!dma->vaddr) + dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr, + GFP_KERNEL | __GFP_ZERO); + if (dma->vaddr) + memset(dma->vaddr, 0, size); + return dma->vaddr ? 0 : -ENOMEM; +} + +static inline struct control_buffer *__dma_get_ctl_buf(struct issei_dma *dma) +{ + return dma->vaddr + dma->length.h2f + dma->length.f2h; +} + +static bool __issei_dma_is_read_busy(struct issei_dma *dma) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(dma); + + return ctl->f2h_counter_wr != ctl->f2h_counter_rd; +} + +static bool __issei_dma_is_write_busy(struct issei_dma *dma) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(dma); + + return ctl->h2f_counter_wr != ctl->h2f_counter_rd; +} + +static void __issei_dma_read_finalize(struct issei_device *idev) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + + dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->f2h_counter_rd++; +} + +static void __issei_dma_write_finalize(struct issei_device *idev) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + + dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->h2f_counter_wr++; +} + +/** + * issei_dma_write - write data package to DMA + * @idev: issei device object + * @data: data atructure + * + * Return: 0 on success, <0 on failures + */ +int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data) +{ + u8 *write_buf = idev->dma.vaddr; + struct ham_message_header *hdr = (struct ham_message_header *)write_buf; + + if (data->length > idev->dma.length.h2f - sizeof(*hdr)) { + dev_err(&idev->dev, "Message is too big\n"); + return -EMSGSIZE; + } + + if (__issei_dma_is_write_busy(&idev->dma)) { + if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) { + dev_err(&idev->dev, "Write stuck in queue\n"); + return -EIO; + } + dev_info(&idev->dev, "Write is busy\n"); + return -EBUSY; + } + + hdr->length = data->length; + hdr->fw_id = data->fw_id; + hdr->host_id = data->host_id; + hdr->flags = data->flags; + hdr->status = data->status; + hdr->reserved = 0; + + memcpy(write_buf + sizeof(*hdr), data->buf, data->length); + + __issei_dma_write_finalize(idev); + idev->last_write_ts = ktime_get(); + return 0; +} + +/** + * issei_dma_read - read data package from DMA + * @idev: issei device object + * @data: data atructure + * + * Return: %0 on success, <0 on failures + */ +int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data) +{ + u8 *read_buf = idev->dma.vaddr + idev->dma.length.h2f; + struct ham_message_header *hdr = (struct ham_message_header *)read_buf; + + if (!__issei_dma_is_read_busy(&idev->dma)) { + dev_dbg(&idev->dev, "Nothing to read\n"); + return -ENODATA; + } + + dev_dbg(&idev->dev, "Reading header\n"); + data->length = hdr->length; + data->fw_id = hdr->fw_id; + data->host_id = hdr->host_id; + data->flags = hdr->flags; + data->status = hdr->status; + + if (data->length > idev->dma.length.f2h - sizeof(*hdr)) { + dev_err(&idev->dev, "Message length %u is bigger than buffer %zu\n", + data->length, idev->dma.length.f2h - sizeof(*hdr)); + return -EIO; + } + + dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length); + data->buf = kmemdup(read_buf + sizeof(*hdr), data->length, GFP_KERNEL); + if (!data->buf) + return -ENOMEM; + __issei_dma_read_finalize(idev); + return 0; +} diff --git a/drivers/misc/issei/dma.h b/drivers/misc/issei/dma.h new file mode 100644 index 000000000000..e6b7aeb50ae6 --- /dev/null +++ b/drivers/misc/issei/dma.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_DMA_H_ +#define _ISSEI_DMA_H_ + +#include <linux/types.h> + +struct issei_device; + +/** + * struct issei_dma_length - sizes of DMA memory portions + * @h2f: host to firmware buffer size + * @f2h: firmware to host buffer size + * @ctl: control buffer size + */ +struct issei_dma_length { + size_t h2f; + size_t f2h; + size_t ctl; +}; + +/** + * struct issei_dma - DMA memory structure + * @vaddr: virtual address + * @daddr: physical address + * @length: memory sizes structure + */ +struct issei_dma { + void *vaddr; + dma_addr_t daddr; + struct issei_dma_length length; +}; + +/* Operation statuses */ +#define HAMS_SUCCESS 0x00 +#define HAMS_PROTOCOL_NOT_SUPPORTED 0x01 +#define HAMS_DEPRECATED_BUS_MSG 0x02 +#define HAMS_CLIENT_NOT_EXISTS 0x03 +#define HAMS_MSG_TOO_BIG 0x04 +#define HAMS_MSG_NOT_CONSUMED 0x05 +#define HAMS_CORRUPTED_BUS_MSG 0x06 +#define HAMS_CORRUPTED_HEADER 0x07 +#define HAMS_INVALID_LENGTH 0x08 +#define HAMS_SHARED_MEMORY_SIZE_UNSUPPORTED 0x09 +#define HAMS_GENERAL_FATAL_ERROR 0xff + +/** + * struct issei_dma_data - data passed through channel + * @fw_id: firmware client id + * @host_id: host client id + * @flags: flags bitmap + * @status: operation status + * @length: data length + * @buf: pointer to data buffer + */ +struct issei_dma_data { + u16 fw_id; + u16 host_id; + u32 flags; + u32 status; + u32 length; + void *buf; +}; + +int issei_dmam_setup(struct issei_device *idev); +int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data); +int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data); + +#endif /*_ISSEI_DMA_H_*/ diff --git a/drivers/misc/issei/fw_client.c b/drivers/misc/issei/fw_client.c new file mode 100644 index 000000000000..b8e48dbf1c9c --- /dev/null +++ b/drivers/misc/issei/fw_client.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include <linux/bug.h> +#include <linux/cleanup.h> +#include <linux/container_of.h> +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/kobject.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/sprintf.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/uuid.h> + +#include "issei_dev.h" +#include "fw_client.h" + +/* + * Specific attribute handlers for fw_clients kset. + * Provide only show function as all fw_client attributes are read-only. + */ + +struct issei_fw_cl_attr { + struct attribute attr; + ssize_t (*show)(struct issei_fw_client *fw_cl, const struct issei_fw_cl_attr *attr, + char *buf); +}; +#define to_issei_fw_cl_attr(x) container_of_const(x, struct issei_fw_cl_attr, attr) + +static ssize_t fw_cl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + const struct issei_fw_cl_attr *issei_attr; + struct issei_fw_client *fw_cl; + + issei_attr = to_issei_fw_cl_attr(attr); + fw_cl = to_issei_fw_client(kobj); + + if (!issei_attr->show) + return -EIO; + + return issei_attr->show(fw_cl, issei_attr, buf); +} + +static const struct sysfs_ops fw_cl_sysfs_ops = { + .show = fw_cl_attr_show, +}; + +#define FW_CL_ATTR_RO(_name) \ + struct issei_fw_cl_attr fw_cl_attr_##_name = __ATTR_RO(_name) + +/* fw_client attributes */ + +static ssize_t id_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->id); +} +static FW_CL_ATTR_RO(id); + +static ssize_t ver_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->ver); +} +static FW_CL_ATTR_RO(ver); + +static ssize_t uuid_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%pUb\n", &fw_cl->uuid); +} +static FW_CL_ATTR_RO(uuid); + +static ssize_t mtu_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->mtu); +} +static FW_CL_ATTR_RO(mtu); + +static const struct attribute *const fw_cl_attrs[] = { + &fw_cl_attr_id.attr, + &fw_cl_attr_ver.attr, + &fw_cl_attr_uuid.attr, + &fw_cl_attr_mtu.attr, + NULL, +}; + +static const struct attribute_group fw_cl_group = { + .attrs_const = fw_cl_attrs, +}; +__ATTRIBUTE_GROUPS(fw_cl); + +static void issei_fw_cl_init(struct issei_fw_client *fw_cl, u16 id, u8 ver, const uuid_t *uuid, + u32 mtu, u32 flags) +{ + INIT_LIST_HEAD(&fw_cl->list); + fw_cl->id = id; + fw_cl->ver = ver; + fw_cl->uuid = *uuid; + fw_cl->mtu = mtu; + fw_cl->flags = flags; +} + +static void fw_cl_release(struct kobject *kobj) +{ + struct issei_fw_client *fw_cl = to_issei_fw_client(kobj); + + kfree(fw_cl); +} + +static const struct kobj_type fw_client_ktype = { + .sysfs_ops = &fw_cl_sysfs_ops, + .release = fw_cl_release, + .default_groups = fw_cl_groups, +}; + +/** + * issei_fw_cl_create - create firmware client object and add to list + * @idev: issei device object + * @id: firmware client id + * @ver: firmware client version + * @uuid: firmware client unique id + * @mtu: firmware client maximum message size + * @flags: firmware client flags + * + * Should be called under idev->client_lock + * + * Return: pointer to newly created object on success, ERR_PTR on failure + */ +struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver, + const uuid_t *uuid, u32 mtu, u32 flags) +{ + int ret; + struct issei_fw_client *fw_cl = kzalloc_obj(*fw_cl); + + if (!fw_cl) + return ERR_PTR(-ENOMEM); + + WARN_ON(!mutex_is_locked(&idev->client_lock)); + + issei_fw_cl_init(fw_cl, id, ver, uuid, mtu, flags); + fw_cl->kobj.kset = idev->fw_clients; + + ret = kobject_init_and_add(&fw_cl->kobj, &fw_client_ktype, NULL, "%u", id); + if (ret) { + kobject_put(&fw_cl->kobj); + return ERR_PTR(ret); + } + + list_add_tail(&fw_cl->list, &idev->fw_client_list); + + dev_dbg(&idev->dev, "FW client %pUb created\n", uuid); + + kobject_uevent(&fw_cl->kobj, KOBJ_ADD); + + return fw_cl; +} + +static void __issei_fw_cl_remove(struct issei_device *idev, struct issei_fw_client *fw_cl) +{ + WARN(fw_cl->cl, "Removing connected client!\n"); + + dev_dbg(&idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid); + + list_del(&fw_cl->list); + kobject_put(&fw_cl->kobj); +} + +/** + * issei_fw_cl_remove_all - remove all firmware client objects + * @idev: issei device object + */ +void issei_fw_cl_remove_all(struct issei_device *idev) +{ + struct issei_fw_client *fw_cl, *next; + + guard(mutex)(&idev->client_lock); + + list_for_each_entry_safe(fw_cl, next, &idev->fw_client_list, list) + __issei_fw_cl_remove(idev, fw_cl); +} + +/** + * issei_fw_cl_find_by_uuid - find firmware client by uuid + * @idev: issei device object + * @uuid: uuid to search by it + * + * Should be called under idev->client_lock + * + * Return: pointer to firmware client object if found, NULL on failure + */ +struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid) +{ + struct issei_fw_client *fw_cl; + + WARN_ON(!mutex_is_locked(&idev->client_lock)); + + list_for_each_entry(fw_cl, &idev->fw_client_list, list) { + if (uuid_equal(&fw_cl->uuid, uuid)) { + kobject_get(&fw_cl->kobj); + return fw_cl; + } + } + return NULL; +} + +/** + * issei_fw_cl_connect - connect firmware and host client + * @fw_cl: firmware client + * @cl: host client + * + * Should be called under idev->client_lock + * + * Return: 0 on success, -EBUSY if already connected + */ +int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl) +{ + if (fw_cl->cl) + return -EBUSY; + + kobject_get(&fw_cl->kobj); + fw_cl->cl = cl; + return 0; +} + +/** + * issei_fw_cl_disconnect - disconnect firmware and host client + * @fw_cl: firmware client + * + * Should be called under idev->client_lock + */ +void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl) +{ + WARN_ON(!fw_cl->cl); + + fw_cl->cl = NULL; + kobject_put(&fw_cl->kobj); +} diff --git a/drivers/misc/issei/fw_client.h b/drivers/misc/issei/fw_client.h new file mode 100644 index 000000000000..377f733d7e91 --- /dev/null +++ b/drivers/misc/issei/fw_client.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_FW_CLIENT_H_ +#define _ISSEI_FW_CLIENT_H_ + +#include <linux/device.h> +#include <linux/kobject.h> +#include <linux/types.h> +#include <linux/uuid.h> + +struct issei_device; +struct issei_host_client; + +/** + * struct issei_fw_client - represents firmware queue + * @kobj: associated kobject + * @list: link in firmware clients list + * @id: firmware client id + * @ver: firmware client version + * @uuid: firmware client protocol id + * @mtu: firmware client maximum buffer size + * @flags: firmware client flags + * @cl: pointer to host client, if connected + */ +struct issei_fw_client { + struct kobject kobj; + struct list_head list; + u16 id; + u8 ver; + uuid_t uuid; + u32 mtu; + u32 flags; + struct issei_host_client *cl; +}; +#define to_issei_fw_client(x) container_of(x, struct issei_fw_client, kobj) + +struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver, + const uuid_t *uuid, u32 mtu, u32 flags); +void issei_fw_cl_remove_all(struct issei_device *idev); +struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid); + +int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl); +void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl); + +#endif /* _ISSEI_FW_CLIENT_H_ */ diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c new file mode 100644 index 000000000000..674d9733d16c --- /dev/null +++ b/drivers/misc/issei/ham.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include <linux/dev_printk.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include "dma.h" +#include "fw_client.h" +#include "ham.h" +#include "hw_msg.h" +#include "issei_dev.h" + +static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf) +{ + struct issei_dma_data data = { }; + int ret; + + data.length = length; + data.buf = buf; + ret = issei_dma_write(idev, &data); + if (ret) + return ret; + return idev->ops->irq_write_generate(idev); +} + +/** + * issei_ham_send_start_req - send start request to firmware + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_send_start_req(struct issei_device *idev) +{ + struct ham_start_message_req req; + + req.header.cmd = HAM_BUS_CMD_START_REQ; + req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER; + req.heci_capabilities_length = 0; + + return __issei_ham_send_msg(idev, sizeof(req), &req); +} + +/** + * issei_ham_send_clients_req - send clients request to firmware + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_send_clients_req(struct issei_device *idev) +{ + struct ham_get_clients_req req; + + req.header.cmd = HAM_BUS_CMD_CLIENT_REQ; + + return __issei_ham_send_msg(idev, sizeof(req), &req); +} + +static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + struct ham_start_message_res *res = (struct ham_start_message_res *)buf; + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_START) { + dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_START); + return -EPROTO; + } + + if (length < sizeof(*res)) { + dev_err(&idev->dev, "Small start response size %zu < %zu\n", + length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->heci_capabilities_length) { + dev_err(&idev->dev, "Wrong start response size %zu != %u\n", + length - sizeof(*res), res->heci_capabilities_length); + return -EPROTO; + } + + memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version)); + idev->fw_protocol_ver = res->supported_version; + dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver, + idev->fw_version[0], idev->fw_version[1], + idev->fw_version[2], idev->fw_version[3]); + + ret = issei_ham_send_clients_req(idev); + if (ret == -EBUSY) + ret = 0; + + return ret; +} + +static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf; + struct ham_client_properties *client; + + if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) { + dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM); + return -EPROTO; + } + + if (length < sizeof(*res)) { + dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) { + dev_err(&idev->dev, "Wrong response size %zu < %zu\n", + length - sizeof(*res), + res->client_count * sizeof(struct ham_client_properties)); + return -EPROTO; + } + + guard(mutex)(&idev->client_lock); + + for (size_t i = 0; i < res->client_count; i++) { + client = &res->clients_props[i]; + dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u", + client->client_number, client->protocol_ver, &client->client_uuid, + client->client_mtu, client->flags); + issei_fw_cl_create(idev, client->client_number, client->protocol_ver, + &client->client_uuid, client->client_mtu, client->flags); + } + return 0; +} + +static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + struct ham_bus_message *hdr = (struct ham_bus_message *)buf; + + if (length < sizeof(*hdr)) { + dev_err(&idev->dev, "Small bus message size %zu < %zu\n", + length, sizeof(*hdr)); + return -EPROTO; + } + + switch (hdr->cmd) { + case HAM_BUS_CMD_START_RSP: + return issei_ham_start_rsp(idev, buf, length); + + case HAM_BUS_CMD_CLIENT_RSP: + return issei_ham_client_rsp(idev, buf, length); + + default: + dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd); + return -EPROTO; + } +} + +/** + * issei_ham_process_ham_rsp - process response from firmware and release buffer + * @idev: issei device object + * @buf: response buffer + * @length: response buffer length + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + int ret; + + ret = __issei_ham_process_ham_rsp(idev, buf, length); + kfree(buf); + return ret; +} diff --git a/drivers/misc/issei/ham.h b/drivers/misc/issei/ham.h new file mode 100644 index 000000000000..37ea64bb2920 --- /dev/null +++ b/drivers/misc/issei/ham.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HAM_H_ +#define _ISSEI_HAM_H_ + +#include <linux/types.h> + +struct issei_device; + +int issei_ham_send_start_req(struct issei_device *idev); +int issei_ham_send_clients_req(struct issei_device *idev); + +static inline bool issei_is_ham_rsp(u16 fw_id, u16 host_id) +{ + return fw_id == 0 && host_id == 0; +} + +int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length); + +#endif /* _ISSEI_HAM_H_ */ diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c new file mode 100644 index 000000000000..8f36d1c319ec --- /dev/null +++ b/drivers/misc/issei/host_client.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include <linux/cleanup.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/kobject.h> +#include <linux/list.h> +#include <linux/overflow.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/uuid.h> + +#include "fw_client.h" +#include "host_client.h" +#include "issei_dev.h" + +static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl) +{ + return cl->fw_cl ? cl->fw_cl->id : 0; +} + +#define ISSEI_CL_FMT "cl:host=%02d fw=%02d " + +#define cl_dbg(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ = _cl_; \ + dev_dbg(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +#define cl_warn(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ = _cl_; \ + dev_warn(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +#define cl_err(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ = _cl_; \ + dev_err(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +static void __issei_cl_clean_wbuf(struct issei_write_buf *wbuf) +{ + list_del(&wbuf->list); + kfree(wbuf->data); + kfree(wbuf); +} + +static void __issei_cl_release_rbuf(struct issei_host_client *cl) +{ + cl->read_data = NULL; + cl->read_data_size = 0; +} + +static void __issei_cl_clean_rbuf(struct issei_host_client *cl) +{ + kfree(cl->read_data); + __issei_cl_release_rbuf(cl); +} + +static void __issei_cl_clean_all_wbuf(struct issei_device *idev, struct issei_host_client *cl) +{ + struct issei_write_buf *wbuf, *next; + + if (!cl->write_in_progress) + return; + list_for_each_entry_safe(wbuf, next, &idev->write_queue, list) { + if (wbuf->cl == cl) { + __issei_cl_clean_wbuf(wbuf); + break; + } + } + cl->write_in_progress = false; + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->write_wait)) + wake_up_interruptible(&cl->write_wait); +} + +static struct issei_host_client *__issei_cl_by_id(struct issei_device *idev, u16 id) +{ + struct issei_host_client *cl; + + list_for_each_entry(cl, &idev->host_client_list, list) { + if (cl->id == id) + return cl; + } + return NULL; +} + +static void __issei_cl_disconnect(struct issei_device *idev, struct issei_host_client *cl) +{ + if (cl->state == ISSEI_HOST_CL_STATE_DISCONNECTED) + return; + + __issei_cl_clean_all_wbuf(idev, cl); + + if (!WARN_ON(!cl->fw_cl)) { + issei_fw_cl_disconnect(cl->fw_cl); + cl->fw_cl = NULL; + } + cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED; + + if (cl->read_data) + __issei_cl_clean_rbuf(cl); + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->read_wait)) + wake_up_interruptible(&cl->read_wait); + cl_dbg(idev, cl, "Disconnected\n"); +} + +static void __issei_cl_init(struct issei_host_client *cl, struct issei_device *idev, + u16 id, struct file *fp) +{ + INIT_LIST_HEAD(&cl->list); + cl->idev = idev; + cl->id = id; + cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED; + cl->fp = fp; + init_waitqueue_head(&cl->write_wait); + init_waitqueue_head(&cl->read_wait); + __issei_cl_release_rbuf(cl); +} + +/** + * issei_cl_create - create the host client + * @idev: issei device + * @fp: file pointer to associate with host client + * + * Return: client pointer on success, ERR_PTR on error + */ +struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp) +{ + struct issei_host_client *cl; + u16 id; + + guard(mutex)(&idev->client_lock); + + if (idev->host_client_count == ISSEI_HOST_CLIENTS_MAX) { + dev_err(&idev->dev, "Maximum open clients %d is reached.\n", + ISSEI_HOST_CLIENTS_MAX); + return ERR_PTR(-EMFILE); + } + + do { + if (check_add_overflow(idev->host_client_last_id, 1, &id)) /* overflow */ + id = 1; + idev->host_client_last_id = id; + /* Not an endless loop as we have less clients then id's */ + } while (__issei_cl_by_id(idev, id)); + + cl = kzalloc_obj(*cl); + if (!cl) + return ERR_PTR(-ENOMEM); + + __issei_cl_init(cl, idev, id, fp); + list_add_tail(&cl->list, &idev->host_client_list); + idev->host_client_count++; + + cl_dbg(idev, cl, "Created\n"); + return cl; +} + +/** + * issei_cl_remove - disconnect and free the host client + * @cl: host client + */ +void issei_cl_remove(struct issei_host_client *cl) +{ + struct issei_device *idev; + + /* don't shout on error exit path */ + if (!cl) + return; + + idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + idev->host_client_count--; + list_del(&cl->list); + + __issei_cl_disconnect(idev, cl); + + cl_dbg(idev, cl, "Removed\n"); + kfree(cl); +} + +/** + * issei_cl_connect - connect between FW and host client + * @cl: host client + * @uuid: FW client unique ID + * @mtu: memory for FW client max message size + * @ver: memory for FW client version + * @flags: memory for FW client flags + * + * Search for firmware client by UUID and connect it to provided + * host client, if not already connected to some client. + * + * Return: 0 on success, <0 on error + */ +int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver, + u32 *flags) +{ + struct issei_device *idev = cl->idev; + struct issei_fw_client *fw_cl; + int ret; + + guard(mutex)(&idev->client_lock); + + if (cl->state == ISSEI_HOST_CL_STATE_CONNECTED) { + cl_err(idev, cl, "Already connected\n"); + return -EISCONN; + } + + fw_cl = issei_fw_cl_find_by_uuid(idev, uuid); + if (!fw_cl) { + cl_dbg(idev, cl, "FW client %pUb not found\n", uuid); + return -ENOTTY; + } + + ret = issei_fw_cl_connect(fw_cl, cl); /* calls kobject_get for fw_cl on success */ + kobject_put(&fw_cl->kobj); + if (ret) { + cl_err(idev, cl, "FW client is already connected ret = %d\n", ret); + return ret; + } + + cl->fw_cl = fw_cl; + cl->state = ISSEI_HOST_CL_STATE_CONNECTED; + + *mtu = fw_cl->mtu; + *ver = fw_cl->ver; + *flags = fw_cl->flags; + cl_dbg(idev, cl, "Connected\n"); + return 0; +} + +/** + * issei_cl_disconnect - disconnect between FW and host client + * @cl: host client + * + * Return: 0 on success, -ENOTCONN if not connected + */ +int issei_cl_disconnect(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + __issei_cl_disconnect(idev, cl); + return 0; +} + +/** + * issei_cl_all_disconnect - disconnect all FW clients + * @idev: issei device + */ +void issei_cl_all_disconnect(struct issei_device *idev) +{ + struct issei_host_client *cl; + + guard(mutex)(&idev->client_lock); + + list_for_each_entry(cl, &idev->host_client_list, list) + __issei_cl_disconnect(idev, cl); +} + +/** + * issei_cl_write - enqueue write request + * @cl: host client + * @buf: buffer to write + * @buf_size: buffer size + * + * Add write request to the write queue and wakes working thread. + * This call takes ownership of buf memory, if succeeded. + * + * Return: size of data on success, <0 on error + */ +ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size) +{ + struct issei_device *idev = cl->idev; + struct issei_write_buf *wbuf; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + + if (cl->write_in_progress) { + cl_dbg(idev, cl, "Another write is in progress\n"); + return -EAGAIN; + } + + if (buf_size > cl->fw_cl->mtu) { + cl_err(idev, cl, "Write is too big %zu > %u\n", buf_size, cl->fw_cl->mtu); + return -EFBIG; + } + + wbuf = kmalloc_obj(*wbuf); + if (!wbuf) + return -ENOMEM; + wbuf->cl = cl; + wbuf->data = buf; + wbuf->data_size = buf_size; + list_add_tail(&wbuf->list, &idev->write_queue); + cl->write_in_progress = true; + cl_dbg(idev, cl, "Write queued %zu bytes\n", buf_size); + + issei_poke_process_thread(idev); + + return buf_size; +} + +/** + * issei_cl_write_from_queue - writes first request from queue to firmware + * @idev: issei device + * + * Tries to write first request from the write queue to firmware. + * Releases buf memory, if succeeded. + * + * Return: 0 on success, <0 on error + */ +int issei_cl_write_from_queue(struct issei_device *idev) +{ + struct issei_write_buf *wbuf; + struct issei_dma_data data; + struct issei_host_client *cl; + int ret; + + guard(mutex)(&idev->client_lock); + + wbuf = list_first_entry_or_null(&idev->write_queue, struct issei_write_buf, list); + if (!wbuf) + return 0; + + cl = wbuf->cl; + + data.fw_id = cl->fw_cl->id; + data.host_id = cl->id; + data.flags = 0; + data.status = 0; + data.length = wbuf->data_size; + data.buf = (void *)wbuf->data; + ret = issei_dma_write(idev, &data); + if (ret == -EBUSY) + return 0; + if (ret == -EIO) + return ret; + if (ret >= 0) + idev->ops->irq_write_generate(idev); + cl->write_in_progress = false; + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->write_wait)) + wake_up_interruptible(&cl->write_wait); + cl_dbg(idev, cl, "Write %zu bytes\n", wbuf->data_size); + __issei_cl_clean_wbuf(wbuf); + return 0; +} + +static struct issei_host_client *__issei_cl_read_buf_check(struct issei_device *idev, u16 fw_id, + u16 host_id, size_t buf_size) +{ + struct issei_host_client *cl; + + cl = __issei_cl_by_id(idev, host_id); + if (!cl) { + dev_dbg(&idev->dev, "No client %u\n", host_id); + return ERR_PTR(-ENOTTY); + } + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) { + cl_dbg(idev, cl, "Not connected\n"); + return ERR_PTR(-ENODEV); + } + if (cl->fw_cl->id != fw_id) { + cl_dbg(idev, cl, "Wrong firmware client %u ?= %u\n", cl->fw_cl->id, fw_id); + return ERR_PTR(-ENODEV); + } + + if (buf_size > cl->fw_cl->mtu) { + cl_err(idev, cl, "Read is too big %zu > %u\n", buf_size, cl->fw_cl->mtu); + __issei_cl_disconnect(idev, cl); + return NULL; + } + + if (cl->read_data) { + cl_err(idev, cl, "Previous data was not read by user-space, disconnecting\n"); + __issei_cl_disconnect(idev, cl); + return NULL; + } + + return cl; +} + +/** + * issei_cl_read_buf - process data from firmware + * @idev: issei device + * @fw_id: firmware client id + * @host_id: host client id + * @buf: buffer with data + * @buf_size: buffer size + * + * Puts data from firmware into provided host client storage. + * Free buffer or consume it. + * + * Return: 0 on success or recoverable error, <0 on unrecoverable error + */ +int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size) +{ + struct issei_host_client *cl; + + guard(mutex)(&idev->client_lock); + + cl = __issei_cl_read_buf_check(idev, fw_id, host_id, buf_size); + if (IS_ERR_OR_NULL(cl)) { + kfree(buf); + return PTR_ERR(cl); + } + + cl->read_data = buf; + cl->read_data_size = buf_size; + + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->read_wait)) + wake_up_interruptible(&cl->read_wait); + cl_dbg(idev, cl, "Read %zu bytes\n", buf_size); + + return 0; +} + +/** + * issei_cl_read - read data from queue to provided buffer + * @cl: host client + * @buf: buffer to store data + * @buf_size: buffer size + * + * Tries to take data buffer from client and return it to caller. + * The caller receives ownership of the data buffer. + * + * Return: read data size on success, <0 on error + */ +ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size) +{ + struct issei_device *idev = cl->idev; + size_t read_data_size; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + + if (!cl->read_data) + return -ENOENT; + + if (cl->read_data_size > buf_size) { + cl_err(idev, cl, "Buffer is too small %zu > %zu\n", + cl->read_data_size, buf_size); + return -EFBIG; + } + + *buf = cl->read_data; + read_data_size = cl->read_data_size; + cl_dbg(idev, cl, "Read by client %zu bytes\n", read_data_size); + __issei_cl_release_rbuf(cl); + return read_data_size; +} + +/** + * issei_cl_check_read - check if client has data to read + * + * @cl: host client + * + * Return: 1 - data available, 0 - no data, < 0 on error + */ +int issei_cl_check_read(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + if (!cl->read_data) + return 0; + return 1; +} + +/** + * issei_cl_check_write - check if client is ready to write + * + * @cl: host client + * + * Return: 1 - can not write, 0 - can write, < 0 on error + */ +int issei_cl_check_write(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + if (cl->write_in_progress) + return 1; + return 0; +} + +void issei_cl_clean_all_wbuf(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + __issei_cl_clean_all_wbuf(idev, cl); +} diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h new file mode 100644 index 000000000000..05e2f4ade9c3 --- /dev/null +++ b/drivers/misc/issei/host_client.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HOST_CLIENT_H_ +#define _ISSEI_HOST_CLIENT_H_ + +#include <linux/types.h> +#include <linux/uuid.h> +#include <linux/wait.h> + +struct file; + +struct issei_device; +struct issei_fw_client; + +/** + * enum issei_host_client_state - host client states + * @ISSEI_HOST_CL_STATE_DISCONNECTED: host client is disconnected + * @ISSEI_HOST_CL_STATE_CONNECTED: host client is connected + */ +enum issei_host_client_state { + ISSEI_HOST_CL_STATE_DISCONNECTED, + ISSEI_HOST_CL_STATE_CONNECTED, +}; + +/** + * struct issei_host_client - represents host client + * @list: link in host clients list + * @idev: issei parent device + * @id: host client id + * @fp: file associated with client + * + * @write_wait: waitqueue for pending write data + * @write_in_progress: indicator for write in process + * + * @state: host client state + * @fw_cl: pointer to firmware client, if connected + * + * @read_wait: waitqueue for read object + * @read_data: received data pointer + * @read_data_size: received data size + */ +struct issei_host_client { + struct list_head list; + struct issei_device *idev; + u16 id; + const struct file *fp; + + wait_queue_head_t write_wait; + bool write_in_progress; + + enum issei_host_client_state state; + struct issei_fw_client *fw_cl; + + wait_queue_head_t read_wait; + u8 *read_data; + size_t read_data_size; +}; + +struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp); +void issei_cl_remove(struct issei_host_client *cl); + +int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver, + u32 *flags); +int issei_cl_disconnect(struct issei_host_client *cl); +void issei_cl_all_disconnect(struct issei_device *idev); + +ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size); +int issei_cl_write_from_queue(struct issei_device *idev); +int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size); +ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size); +int issei_cl_check_read(struct issei_host_client *cl); +int issei_cl_check_write(struct issei_host_client *cl); +void issei_cl_clean_all_wbuf(struct issei_host_client *cl); + +#endif /* ISSEI_HOST_CLIENT_H_ */ diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c new file mode 100644 index 000000000000..501774aa95ff --- /dev/null +++ b/drivers/misc/issei/hw_heci.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include <linux/align.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/pci.h> +#include <linux/sizes.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include "hw_heci.h" +#include "hw_heci_regs.h" +#include "hw_msg.h" + +/** + * heci_reg_read - Reads 32bit data from the issei heci device + * @hw: the heci hardware structure + * @offset: offset from which to read the data + * + * Return: register value (u32) + */ +static inline u32 heci_reg_read(const struct issei_heci_hw *hw, unsigned long offset) +{ + return ioread32(hw->mem_addr + offset); +} + +/** + * heci_reg_write - Writes 32bit data to the issei heci device + * + * @hw: the heci hardware structure + * @offset: offset from which to write the data + * @value: register value to write (u32) + */ +static inline void heci_reg_write(const struct issei_heci_hw *hw, unsigned long offset, u32 value) +{ + iowrite32(value, hw->mem_addr + offset); +} + +/** + * heci_fwcbrw_read - Reads 32bit data from heci circular buffer + * @hw: the heci hardware structure + * + * Return: FW_CB_RW register value (u32) + */ +static inline u32 heci_fwcbrw_read(const struct issei_heci_hw *hw) +{ + return heci_reg_read(hw, FW_CB_RW); +} + +/** + * heci_hcbww_write - write 32bit data to the host circular buffer + * @hw: the heci hardware structure + * @data: 32bit data to be written to the host circular buffer + */ +static inline void heci_hcbww_write(const struct issei_heci_hw *hw, u32 data) +{ + heci_reg_write(hw, H_CB_WW, data); +} + +/** + * heci_irq_src - Filters IRQ source bits from the host CSR + * @hcsr: host CSR register value + * + * Return: interrupt source bits of host CSR + */ +static inline u32 heci_irq_src(u32 hcsr) +{ + return hcsr & H_CSR_IS; +} + +/** + * heci_hcsr_read - Reads 32bit data from the host CSR + * @idev: the device structure + * + * Return: H_CSR register value (u32) + */ +static inline u32 heci_hcsr_read(const struct issei_device *idev) +{ + return heci_reg_read(to_heci_hw(idev), H_CSR); +} + +/** + * heci_hcsr_write - writes H_CSR register to device + * @idev: the device structure + * @reg: new register value + */ +static inline void heci_hcsr_write(struct issei_device *idev, u32 reg) +{ + heci_reg_write(to_heci_hw(idev), H_CSR, reg); +} + +/** + * heci_hcsr_set - writes H_CSR register to the heci device + * @idev: the device structure + * @reg: new register value + * + * Writes H_CSR register to the heci device + * and ignores the H_IS bit for it is write-one-to-zero. + * + */ +static inline void heci_hcsr_set(struct issei_device *idev, u32 reg) +{ + reg &= ~H_CSR_IS; + heci_hcsr_write(idev, reg); +} + +/** + * heci_hcsr_set_hig - set host interrupt (set H_CSR_IG) + * @idev: the device structure + */ +static inline void heci_hcsr_set_hig(struct issei_device *idev) +{ + u32 reg; + + reg = heci_hcsr_read(idev) | H_CSR_IG; + heci_hcsr_set(idev, reg); +} + +/** + * heci_fwcsr_read - Reads 32bit data from the FW CSR + * @idev: the device structure + * + * Return: FW_CSR_HA register value (u32) + */ +static inline u32 heci_fwcsr_read(const struct issei_device *idev) +{ + return heci_reg_read(to_heci_hw(idev), FW_CSR_HA); +} + +/** + * heci_count_full_read_slots - counts read full slots. + * @idev: the device structure + * + * Return: -EOVERFLOW if overflow, otherwise filled slots count + */ +static int heci_count_full_read_slots(struct issei_device *idev) +{ + u8 buffer_depth, filled_slots; + u8 read_ptr, write_ptr; + u32 reg; + + reg = heci_fwcsr_read(idev); + buffer_depth = (u8)FIELD_GET(FW_CSR_CBD, reg); + read_ptr = (u8)FIELD_GET(FW_CSR_CBRP, reg); + write_ptr = (u8)FIELD_GET(FW_CSR_CBWP, reg); + filled_slots = write_ptr - read_ptr; + + /* check for overflow */ + if (filled_slots > buffer_depth) + return -EOVERFLOW; + + dev_dbg(&idev->dev, "filled_slots = %08x\n", filled_slots); + return filled_slots; +} + +/** + * heci_irq_disable - disables heci device interrupts + * @idev: the device structure + * @reg: supplied hcsr register value + * + * disables heci device interrupts using supplied hcsr register value. + */ +static inline void heci_irq_disable(struct issei_device *idev, u32 reg) +{ + reg &= ~H_CSR_IE; + heci_hcsr_set(idev, reg); +} + +/** + * heci_irq_clear - clear and stop interrupts + * @idev: the device structure + * @reg: supplied hcsr register value + */ +static inline void heci_irq_clear(struct issei_device *idev, u32 reg) +{ + if (heci_irq_src(reg)) + heci_hcsr_write(idev, reg); +} + +/** + * issei_heci_irq_clear - clear and stop interrupts + * @idev: the device structure + */ +static void issei_heci_irq_clear(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + heci_irq_clear(idev, reg); +} + +/** + * issei_heci_irq_enable - enables heci device interrupts + * @idev: the device structure + */ +static void issei_heci_irq_enable(struct issei_device *idev) +{ + u32 reg; + + reg = heci_hcsr_read(idev) | H_CSR_IE; + heci_hcsr_set(idev, reg); +} + +/** + * issei_heci_irq_disable - disables heci device interrupts + * @idev: the device structure + */ +static void issei_heci_irq_disable(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + heci_irq_disable(idev, reg); +} + +static void issei_heci_irq_sync(struct issei_device *idev) +{ + synchronize_irq(to_heci_hw(idev)->irq); +} + +/** + * issei_heci_hw_reset_release - release device from the reset + * @idev: the device structure + */ +static void issei_heci_hw_reset_release(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + reg |= H_CSR_IG; + reg &= ~H_CSR_RST; + heci_hcsr_set(idev, reg); +} + +/** + * heci_hw_is_ready - check whether the hw has turned ready + * @idev: the device structure + * + * Return: bool + */ +static bool heci_hw_is_ready(struct issei_device *idev) +{ + u32 reg = heci_fwcsr_read(idev); + + return reg & FW_CSR_RDY; +} + +/** + * heci_hw_is_resetting - check whether the hw is in reset + * @idev: the device structure + * + * Return: bool + */ +static bool heci_hw_is_resetting(struct issei_device *idev) +{ + u32 reg = heci_fwcsr_read(idev); + + return reg & FW_CSR_RST; +} + +/** + * issei_heci_host_set_ready - enable device + * @idev: the device structure + */ +static void issei_heci_host_set_ready(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + reg |= H_CSR_IE | H_CSR_IG | H_CSR_RDY; + heci_hcsr_set(idev, reg); +} + +/** + * issei_heci_hw_reset - resets fw via heci csr register. + * @idev: the device structure + * @enable: if interrupt should be enabled after reset. + * + * Return: 0 on success an error code otherwise + */ +static int issei_heci_hw_reset(struct issei_device *idev, bool enable) +{ + u32 reg; + + if (enable) + issei_heci_irq_enable(idev); + + reg = heci_hcsr_read(idev); + /* + * H_CSR_RST may be found lit before reset is started, + * for example if preceding reset flow hasn't completed. + * In that case asserting H_CSR_RST will be ignored, therefore + * we need to clean H_CSR_RST bit to start a successful reset sequence. + */ + if (reg & H_CSR_RST) { + dev_warn(&idev->dev, "H_CSR_RST is set = 0x%08X", reg); + reg &= ~H_CSR_RST; + heci_hcsr_set(idev, reg); + reg = heci_hcsr_read(idev); + } + + reg |= H_CSR_RST | H_CSR_IG | H_CSR_IS; + + if (!enable) + reg &= ~H_CSR_IE; + + heci_hcsr_write(idev, reg); + + /* + * Host reads the H_CSR once to ensure that the + * posted write to H_CSR completes. + */ + reg = heci_hcsr_read(idev); + + if (!(reg & H_CSR_RST)) + dev_warn(&idev->dev, "H_CSR_RST is not set = 0x%08X", reg); + + if (reg & H_CSR_RDY) + dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg); + + if (!enable) + issei_heci_hw_reset_release(idev); + return 0; +} + +/** + * issei_heci_irq_write_generate - generate interrupt to signal write completion. + * @idev: the device structure + * + * Return: 0 on success, -EIO if hardware is not ready and requires reset + */ +static int issei_heci_irq_write_generate(struct issei_device *idev) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + + scoped_guard(spinlock_irqsave, &hw->access_lock) + heci_hcsr_set_hig(idev); + if (!heci_hw_is_ready(idev)) + return -EIO; + return 0; +} + +/** + * issei_heci_hw_config - initial hardware configuration. + * @idev: the device structure + * + * Return: 0 always + */ +static int issei_heci_hw_config(struct issei_device *idev) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + u32 reg; + + /* Doesn't change in runtime */ + reg = heci_hcsr_read(idev); + hw->hbuf_depth = FIELD_GET(H_CSR_CBD, reg); + + return 0; +} + +/** + * heci_write_hbuf - write to hardware buffer + * @idev: the device structure + * @data: data to write + * @data_len: data size + * + * Return: 0 on success, <0 on error + */ +static int heci_write_hbuf(struct issei_device *idev, const void *data, size_t data_len) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { + dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + const u32 *reg_buf = data; + size_t i; + + for (i = 0; i < data_len / CB_SLOT_SIZE; i++) + heci_hcbww_write(hw, reg_buf[i]); + } + + return issei_heci_irq_write_generate(idev); +} + +/** + * heci_read_hbuf - read data from hardware buffer + * @idev: the device structure + * @data: buffer to store read data + * @data_len: buffer size + * + * Return: 0 on success, <0 on error + */ +static int heci_read_hbuf(struct issei_device *idev, void *data, size_t data_len) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + u32 *reg_buf = data; + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { + dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + for (; data_len >= CB_SLOT_SIZE; data_len -= CB_SLOT_SIZE) + *reg_buf++ = heci_fwcbrw_read(hw); + } + return 0; +} + +/** + * issei_heci_setup_message_send - send setup message to firmware + * @idev: the device structure + * + * Return: 0 on success, <0 on error + */ +static int issei_heci_setup_message_send(struct issei_device *idev) +{ + struct ham_setup_shared_memory_req req = { + .msg_id = HAM_CB_MESSAGE_ID_REQ, + .ver = HAM_CB_MESSAGE_VER, + .reserved = 0, + .buffer_physical_address = idev->dma.daddr, + .host_to_fw_section_length = idev->dma.length.h2f, + .fw_to_host_section_length = idev->dma.length.f2h, + .control_length = idev->dma.length.ctl + }; + int ret; + + ret = heci_write_hbuf(idev, &req, sizeof(req)); + if (ret) + dev_err(&idev->dev, "Shared memory req write failed ret = %d\n", ret); + + return ret; +} + +/** + * issei_heci_setup_message_recv - receive setup message response from firmware + * @idev: the device structure + * + * Return: 0 on success, <0 on error + */ +static int issei_heci_setup_message_recv(struct issei_device *idev) +{ + struct ham_setup_shared_memory_res res; + int ret; + + if (heci_count_full_read_slots(idev) != sizeof(res) / CB_SLOT_SIZE) { + dev_dbg(&idev->dev, "Setup response is not fully received\n"); + return -ENODATA; + } + + ret = heci_read_hbuf(idev, &res, sizeof(res)); + if (ret) { + dev_err(&idev->dev, "Shared memory res read failed ret = %d\n", ret); + return ret; + } + + if (res.msg_id != HAM_CB_MESSAGE_ID_RES) { + dev_err(&idev->dev, "Shared memory res header 0x%x != 0x%x\n", + res.msg_id, HAM_CB_MESSAGE_ID_RES); + return -EPROTO; + } + if (res.status != 0) { + dev_err(&idev->dev, "Shared memory res status %d != 0\n", res.status); + return -EPROTO; + } + + return 0; +} + +static const struct issei_hw_ops hw_heci_ops = { + .irq_clear = issei_heci_irq_clear, + .irq_enable = issei_heci_irq_enable, + .irq_disable = issei_heci_irq_disable, + .irq_sync = issei_heci_irq_sync, + + .hw_reset = issei_heci_hw_reset, + .hw_config = issei_heci_hw_config, + .hw_reset_release = issei_heci_hw_reset_release, + .host_set_ready = issei_heci_host_set_ready, + + .hw_is_ready = heci_hw_is_ready, + + .setup_message_send = issei_heci_setup_message_send, + .setup_message_recv = issei_heci_setup_message_recv, + .irq_write_generate = issei_heci_irq_write_generate, +}; + +const struct issei_hw_ops *issei_heci_get_ops(void) +{ + return &hw_heci_ops; +} + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id) +{ + struct issei_device *idev = dev_id; + struct issei_heci_hw *hw = to_heci_hw(idev); + u32 reg; + + reg = heci_hcsr_read(idev); + if (!heci_irq_src(reg)) + return IRQ_NONE; + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + reg = heci_hcsr_read(idev); + heci_irq_clear(idev, reg); + + if (heci_hw_is_resetting(idev)) + heci_hcsr_set_hig(idev); + } + + dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg)); + + issei_poke_process_thread(idev); + + return IRQ_HANDLED; +} + +static const struct hw_heci_cfg hw_heci_pch_cfg = { + .dma_length.h2f = SZ_64K, + .dma_length.f2h = SZ_64K, + .dma_length.ctl = SZ_4K, +}; + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx) +{ + return &hw_heci_pch_cfg; +} + +/** + * issei_heci_dev_init - initializes the issei device structure with hw_heci + * @idev: device structure + * @mem_addr: memory address on bar + * @cfg: per device generation config + */ +void issei_heci_dev_init(struct issei_device *idev, + void __iomem *mem_addr, const struct hw_heci_cfg *cfg) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + + spin_lock_init(&hw->access_lock); + hw->mem_addr = mem_addr; + hw->cfg = cfg; +} diff --git a/drivers/misc/issei/hw_heci.h b/drivers/misc/issei/hw_heci.h new file mode 100644 index 000000000000..0ae2faeedf12 --- /dev/null +++ b/drivers/misc/issei/hw_heci.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_HECI_H_ +#define _ISSEI_HW_HECI_H_ +#include <linux/irqreturn.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include "issei_dev.h" + +/* + * hw_heci_cfg - issei heci device configuration + * + * @dma_length: DMA area length + */ +struct hw_heci_cfg { + const struct issei_dma_length dma_length; +}; + +/** + * struct issei_heci_hw - issei heci hw specific data + * + * @cfg: per device generation config and ops + * @mem_addr: io memory address + * @irq: device irq number + * @access_lock: spinlock to protect hw access + * @hbuf_depth: depth of hardware host/write buffer in slots + */ +struct issei_heci_hw { + const struct hw_heci_cfg *cfg; + void __iomem *mem_addr; + int irq; + spinlock_t access_lock; + u8 hbuf_depth; +}; + +#define to_heci_hw(dev) ((struct issei_heci_hw *)(dev)->hw) + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx); +const struct issei_hw_ops *issei_heci_get_ops(void); + +void issei_heci_dev_init(struct issei_device *idev, + void __iomem *mem_addr, const struct hw_heci_cfg *cfg); + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id); + +#endif /* _ISSEI_HW_HECI_H_ */ diff --git a/drivers/misc/issei/hw_heci_regs.h b/drivers/misc/issei/hw_heci_regs.h new file mode 100644 index 000000000000..a991b670a9d3 --- /dev/null +++ b/drivers/misc/issei/hw_heci_regs.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_HECI_REGS_H_ +#define _ISSEI_HW_HECI_REGS_H_ + +#include <linux/bits.h> + +/* H_CB_WW - Host Circular Buffer (CB) Write Window register */ +#define H_CB_WW 0x0 +/* H_CSR - Host Control Status register */ +#define H_CSR 0x4 +#define H_CSR_CBD GENMASK(31, 24) /* Host Circular Buffer Depth */ +#define H_CSR_CBWP GENMASK(23, 16) /* Host Circular Buffer Write Pointer */ +#define H_CSR_CBRP GENMASK(15, 8) /* Host Circular Buffer Read Pointer */ +#define H_CSR_RST BIT(4) /* Host Reset */ +#define H_CSR_RDY BIT(3) /* Host Ready */ +#define H_CSR_IG BIT(2) /* Host Interrupt Generate */ +#define H_CSR_IS BIT(1) /* Host Interrupt Status */ +#define H_CSR_IE BIT(0) /* Host Interrupt Enable */ +/* FW_CB_RW - FW Circular Buffer Read Window register (read only) */ +#define FW_CB_RW 0x8 +/* FW_CSR_HA - FW Control Status Host Access register (read only) */ +#define FW_CSR_HA 0xC +#define FW_CSR_CBD GENMASK(31, 24) /* FW CB (Circular Buffer) Depth */ +#define FW_CSR_CBWP GENMASK(23, 16) /* FW CB Write Pointer */ +#define FW_CSR_CBRP GENMASK(15, 8) /* FW CB Read Pointer */ +#define FW_CSR_RST BIT(4) /* FW Reset */ +#define FW_CSR_RDY BIT(3) /* FW Ready */ +#define FW_CSR_IG BIT(2) /* FW Interrupt Generate */ +#define FW_CSR_IS BIT(1) /* FW Interrupt Status */ +#define FW_CSR_IE BIT(0) /* FW Interrupt Enable */ + +#define CB_SLOT_SIZE sizeof(u32) /* Circular Buffer windows size */ + +#endif /* _ISSEI_HW_HECI_REGS_H_ */ diff --git a/drivers/misc/issei/hw_msg.h b/drivers/misc/issei/hw_msg.h new file mode 100644 index 000000000000..28fd3775f64c --- /dev/null +++ b/drivers/misc/issei/hw_msg.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_MSG_H_ +#define _ISSEI_HW_MSG_H_ + +#include <linux/types.h> +#include <linux/uuid.h> + +#define HAM_CB_MESSAGE_ID_REQ 0x8086cafe +#define HAM_CB_MESSAGE_ID_RES 0xcafe8086 +#define HAM_CB_MESSAGE_VER 0x1 + +/** + * struct ham_setup_shared_memory_req - shared memory setup request + * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_REQ + * @ver: message version (%HAM_CB_MESSAGE_VER) + * @reserved: reserved + * @buffer_physical_address: physical address of DMA buffer + * @host_to_fw_section_length: memory size for host to fw communication + * @fw_to_host_section_length: memory size for fw to host communication + * @control_length: memory size for control buffer + */ +struct ham_setup_shared_memory_req { + u32 msg_id; + u16 ver; + u16 reserved; + u64 buffer_physical_address; + u32 host_to_fw_section_length; + u32 fw_to_host_section_length; + u32 control_length; +} __packed __aligned(4); + +/** + * struct ham_setup_shared_memory_res - shared memory setup response + * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_RES + * @status: operation status + */ +struct ham_setup_shared_memory_res { + u32 msg_id; + u32 status; +}; + +/** + * struct control_buffer - control buffer structure + * @h2f_counter_wr: write counter host to fw + * @h2f_counter_rd: read counter host to fw + * @f2h_counter_wr: write counter fw to host + * @f2h_counter_rd: read counter fw to host + */ +struct control_buffer { + u32 h2f_counter_wr; + u32 h2f_counter_rd; + u32 f2h_counter_wr; + u32 f2h_counter_rd; +}; + +/* HAM messages over DMA */ + +/** + * struct ham_message_header - message header over DMA + * @length: message length (payload only, not including header) + * @fw_id: firmware client id (0 means Bus Message) + * @host_id: host client id (0 means Bus Message) + * @flags: message flags + * @status: operation status + * @reserved: reserved + */ +struct ham_message_header { + u32 length; + u16 fw_id; + u16 host_id; + u32 flags; + u32 status; + u32 reserved; +}; + +/* Bus Commands */ +#define HAM_BUS_CMD_START_REQ 0x00 +#define HAM_BUS_CMD_START_RSP 0x80 +#define HAM_BUS_CMD_CLIENT_REQ 0x01 +#define HAM_BUS_CMD_CLIENT_RSP 0x81 + +/** + * struct ham_bus_message - bus message header + * @cmd: command code + */ +struct ham_bus_message { + u32 cmd; +}; + +#define HAM_SUPPORTED_VERSION 0x01 + +/** + * struct ham_start_message_req - start message + * @header: bus message header (%HAM_BUS_CMD_START_REQ) + * @supported_version: supported protocol version + * @heci_capabilities_length: protocol capabilities length in bytes + * @heci_capabilities: protocol capabilities data + */ +struct ham_start_message_req { + struct ham_bus_message header; + u16 supported_version; + u8 heci_capabilities_length; + u8 heci_capabilities[] __counted_by(heci_capabilities_length); +} __packed; + +/** + * struct ham_start_message_res - start message response + * @header: bus message header (%HAM_BUS_CMD_START_RSP) + * @fw_version: firmware version (four u16 blocks) + * @supported_version: supported protocol version + * @heci_capabilities_length: protocol capabilities length in bytes + * @heci_capabilities: protocol capabilities data + */ +struct ham_start_message_res { + struct ham_bus_message header; + u16 fw_version[4]; + u16 supported_version; + u8 heci_capabilities_length; + u8 heci_capabilities[] __counted_by(heci_capabilities_length); +} __packed; + +/** + * struct ham_get_clients_req - clients list request + * @header: bus message header (%HAM_BUS_CMD_CLIENT_REQ) + */ +struct ham_get_clients_req { + struct ham_bus_message header; +}; + +/** + * struct ham_client_properties - single client properties + * @client_number: client id in firmware + * @protocol_ver: client protocol version + * @reserved: reserved + * @client_uuid: protocol name (UUID) + * @client_mtu: max message length supported by client + * @flags: client flags + */ +struct ham_client_properties { + u16 client_number; + u8 protocol_ver; + u8 reserved; + uuid_t client_uuid; + u32 client_mtu; + u32 flags; +}; + +/** + * struct ham_get_clients_res - client properties response + * @header: bus message header (%HAM_BUS_CMD_CLIENT_RSP) + * @client_count: number of clients in firmware + * @reserved: reserved + * @clients_props: list of client properties + */ +struct ham_get_clients_res { + struct ham_bus_message header; + u16 client_count; + u16 reserved; + struct ham_client_properties clients_props[] __counted_by(client_count); +}; + +#endif /* _ISSEI_HW_MSG_H_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h new file mode 100644 index 000000000000..b47c4a7c2da4 --- /dev/null +++ b/drivers/misc/issei/issei_dev.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_DEV_H_ +#define _ISSEI_DEV_H_ + +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/sched.h> +#include <linux/time64.h> +#include <linux/types.h> +#include <linux/wait.h> + +#include "dma.h" + +struct cdev; +struct kset; + +struct issei_device; +struct issei_host_client; + +extern struct class *issei_class; + +#define ISSEI_HOST_CLIENTS_MAX 255 + +#define ISSEI_SUPPORTED_PROTOCOL_VER 1 + +#define ISSEI_MAX_CONSEC_RESET 3 + +#define ISSEI_RST_HW_READY_TIMEOUT_MSEC (2 * MSEC_PER_SEC) +#define ISSEI_RST_STEP_TIMEOUT_MSEC (2 * MSEC_PER_SEC) +#define ISSEI_STOP_TIMEOUT_MSEC 500 +#define ISSEI_WRITE_TIMEOUT_MSEC (MSEC_PER_SEC) + +/** + * struct issei_write_buf - write buffer object + * @list: linked list pointer + * @cl: host client that requested this write + * @data: data to write + * @data_size: data size + */ +struct issei_write_buf { + struct list_head list; + struct issei_host_client *cl; + const u8 *data; + size_t data_size; +}; + +/** + * struct issei_hw_ops - callbacks for hardware operations + * @irq_clear: clear irq + * @irq_enable: enable irq + * @irq_disable: disable irq + * @irq_sync: sync irq + * @hw_reset: initiate hardware reset + * @hw_config: initial hardware config + * @hw_is_ready: check if hardware is ready + * @hw_reset_release: release hardware from reset + * @host_set_ready: set host ready indicator + * @setup_message_send: send setup message + * @setup_message_recv: receive setup message + * @irq_write_generate: generate interrupt on write complete + */ +struct issei_hw_ops { + void (*irq_clear)(struct issei_device *idev); + void (*irq_enable)(struct issei_device *idev); + void (*irq_disable)(struct issei_device *idev); + void (*irq_sync)(struct issei_device *idev); + int (*hw_reset)(struct issei_device *idev, bool enable); + int (*hw_config)(struct issei_device *idev); + bool (*hw_is_ready)(struct issei_device *idev); + void (*hw_reset_release)(struct issei_device *idev); + void (*host_set_ready)(struct issei_device *idev); + int (*setup_message_send)(struct issei_device *idev); + int (*setup_message_recv)(struct issei_device *idev); + int (*irq_write_generate)(struct issei_device *idev); +}; + +/** + * enum issei_rst_state: driver reset flow states + * @ISSEI_RST_STATE_INIT: initial state + * @ISSEI_RST_STATE_HW_READY: waiting for HW to be ready + * @ISSEI_RST_STATE_SETUP: waiting for channel setup completion + * @ISSEI_RST_STATE_START: waiting for start handshake completion + * @ISSEI_RST_STATE_CLIENT_ENUM: waiting for client enumeration + * @ISSEI_RST_STATE_DONE: reset flow is done + * @ISSEI_RST_STATE_DISABLED: flow is disabled + */ +enum issei_rst_state { + ISSEI_RST_STATE_INIT, + ISSEI_RST_STATE_HW_READY, + ISSEI_RST_STATE_SETUP, + ISSEI_RST_STATE_START, + ISSEI_RST_STATE_CLIENT_ENUM, + ISSEI_RST_STATE_DONE, + ISSEI_RST_STATE_DISABLED, +}; + +/** + * struct issei_device - issei device + * @parent: parent device object + * @dev: associated device object + * @cdev: character device + * @minor: allocated minor number + * @wait_has_data: wait queue for data + * @has_data: there are data to process + * @power_down: device is powering down + * @wait_rst_state: waitqueue for reset state processing + * @rst_state: reset state + * @fw_protocol_ver: protocol version + * @fw_version: firmware version + * @process_thread: worker thread + * @reset_count: number of consecutive link reset attempts + * @all_reset_count: cumilative number of link reset attempts + * @client_lock: mutex to protect client lists and write queue + * @host_client_list: host clients list + * @host_client_last_id: last allocated host client id + * @host_client_count: number of active host clients + * @fw_client_list: firmware clients list + * @write_queue: write queue + * @last_write_ts: last write timestamp + * @dma: DMA memory configuration + * @ops: hardware operations + * @hw: hw-specific data + */ +struct issei_device { + struct device *parent; + struct device dev; + struct cdev *cdev; + u32 minor; + wait_queue_head_t wait_has_data; + bool has_data; + bool power_down; + wait_queue_head_t wait_rst_state; + enum issei_rst_state rst_state; + u16 fw_protocol_ver; + u16 fw_version[4]; + /* reset flow */ + struct task_struct *process_thread; + u8 reset_count; + u8 all_reset_count; + /* clients */ + struct mutex client_lock; + struct list_head host_client_list; + u16 host_client_last_id; + u8 host_client_count; + struct kset *fw_clients; + struct list_head fw_client_list; + struct list_head write_queue; + ktime_t last_write_ts; + struct issei_dma dma; + const struct issei_hw_ops *ops; + char hw[]; +}; + +static inline void issei_poke_process_thread(struct issei_device *idev) +{ + WRITE_ONCE(idev->has_data, true); + wake_up_interruptible(&idev->wait_has_data); +} + +int issei_start(struct issei_device *idev); +void issei_stop(struct issei_device *idev); +#endif /* _ISSEI_DEV_H_ */ diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c new file mode 100644 index 000000000000..5987fb340250 --- /dev/null +++ b/drivers/misc/issei/main.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include <linux/atomic.h> +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/jiffies.h> +#include <linux/kthread.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include "cdev.h" +#include "fw_client.h" +#include "host_client.h" +#include "ham.h" +#include "issei_dev.h" + +static void issei_rst_state_set(struct issei_device *idev, enum issei_rst_state state) +{ + idev->rst_state = state; + /* wake up the thread */ + if (waitqueue_active(&idev->wait_rst_state)) + wake_up(&idev->wait_rst_state); +} + +static int issei_reset(struct issei_device *idev) +{ + int ret; + + idev->ops->irq_clear(idev); + + issei_cl_all_disconnect(idev); + issei_fw_cl_remove_all(idev); + /* No need to check for overflow here, the counter is used only for info */ + idev->all_reset_count++; + ret = idev->ops->hw_reset(idev, !idev->power_down); + issei_dmam_setup(idev); + if (ret) { + dev_err(&idev->dev, "hw_reset failed ret = %d\n", ret); + return ret; + } + + if (idev->power_down) { + dev_dbg(&idev->dev, "powering down: end of reset\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + return -ENODEV; + } + return 0; +} + +static int issei_process_read_msg(struct issei_device *idev) +{ + struct issei_dma_data data = {}; + int ret; + + ret = issei_dma_read(idev, &data); + if (ret) + return ret; + + dev_dbg(&idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id, + data.status, data.length); + if (data.status != HAMS_SUCCESS) { + dev_err(&idev->dev, "Command failed with status 0x%02X", data.status); + kfree(data.buf); + ret = -EIO; + } else { + if (issei_is_ham_rsp(data.fw_id, data.host_id)) + ret = issei_ham_process_ham_rsp(idev, data.buf, data.length); + else + ret = issei_cl_read_buf(idev, data.fw_id, data.host_id, + data.buf, data.length); + } + idev->ops->irq_write_generate(idev); + return ret; +} + +static int issei_process_write_msg(struct issei_device *idev) +{ + if (idev->rst_state != ISSEI_RST_STATE_DONE) + return 0; + + return issei_cl_write_from_queue(idev); +} + +static int issei_process_thread(void *_dev) +{ + long timeout, old_timeout = MAX_SCHEDULE_TIMEOUT; + struct issei_device *idev = _dev; + int ret; + + while (!kthread_should_stop()) { + dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state); + if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) { + if (!idev->power_down) + dev_dbg(&idev->dev, "HW not ready, resetting\n"); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + if (idev->power_down) + idev->rst_state = ISSEI_RST_STATE_INIT; + WRITE_ONCE(idev->has_data, false); + dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state); + timeout = MAX_SCHEDULE_TIMEOUT; + ret = 0; + switch (idev->rst_state) { + case ISSEI_RST_STATE_DISABLED: + if (idev->power_down) { + dev_dbg(&idev->dev, "Interrupt in power down?\n"); + break; + } + idev->rst_state = ISSEI_RST_STATE_INIT; + fallthrough; + + case ISSEI_RST_STATE_INIT: + idev->ops->irq_clear(idev); + idev->ops->irq_sync(idev); + + if (!idev->power_down) { + idev->reset_count++; + if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) { + dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + break; + } + } + + ret = issei_reset(idev); + if (idev->power_down) { + dev_dbg(&idev->dev, "Powering down\n"); + return 0; + } + if (ret) + break; + + idev->rst_state = ISSEI_RST_STATE_HW_READY; + timeout = msecs_to_jiffies(ISSEI_RST_HW_READY_TIMEOUT_MSEC); + break; + + case ISSEI_RST_STATE_HW_READY: + if (!idev->ops->hw_is_ready(idev)) { + dev_dbg(&idev->dev, "HW is not ready?\n"); + timeout = old_timeout; + break; + } + + dev_dbg(&idev->dev, "HW is ready\n"); + idev->ops->hw_reset_release(idev); + idev->ops->host_set_ready(idev); + ret = idev->ops->setup_message_send(idev); + if (ret) + break; + + idev->rst_state = ISSEI_RST_STATE_SETUP; + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + break; + + case ISSEI_RST_STATE_SETUP: + ret = idev->ops->setup_message_recv(idev); + if (ret) { + if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; + } + } else { + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + ret = issei_ham_send_start_req(idev); + idev->rst_state = ISSEI_RST_STATE_START; + } + break; + + case ISSEI_RST_STATE_START: + ret = issei_process_read_msg(idev); + if (!ret) { + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + idev->rst_state = ISSEI_RST_STATE_CLIENT_ENUM; + } else if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; + } + break; + + case ISSEI_RST_STATE_CLIENT_ENUM: + ret = issei_process_read_msg(idev); + if (ret) { + if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; + } + } else { + idev->reset_count = 0; + idev->rst_state = ISSEI_RST_STATE_DONE; + dev_dbg(&idev->dev, "Reset finished successfully\n"); + } + break; + + case ISSEI_RST_STATE_DONE: + ret = issei_process_read_msg(idev); + if (ret != 0 && ret != -ENODATA) + break; + + ret = issei_process_write_msg(idev); + break; + } + + if (ret) { + dev_warn(&idev->dev, "Process failed ret = %d\n", ret); + idev->rst_state = ISSEI_RST_STATE_INIT; + continue; + } + + /* + * Every thread that has data to process sets the 'has_data' flag and + * triggers the wait queue. + * The processing thread, in each loop iteration, resets 'has_data' + * and processes all available data. + * + * After processing, the thread waits for 'has_data' to be set again. + * + * If the wait function times out but 'has_data' becomes 1 before + * the subsequent atomic read check, this is acceptable from a flow + * perspective - the thread will continue processing the data. + * + * The 'has_data' flag cannot become 0 between the wait function and + * the atomic read check, since only this thread is allowed to reset it to 0. + */ + + old_timeout = wait_event_interruptible_timeout(idev->wait_has_data, + READ_ONCE(idev->has_data), + timeout); + if (idev->rst_state == ISSEI_RST_STATE_DISABLED) + continue; + + if (!READ_ONCE(idev->has_data)) { + dev_warn(&idev->dev, "Timed out at state %d, resetting\n", + idev->rst_state); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + } + + return 0; +} + +/** + * issei_start - configure HW device and start processing thread. + * @idev: the device structure + * + * Return: 0 on success, < 0 on failure + */ +int issei_start(struct issei_device *idev) +{ + int ret; + + idev->power_down = false; + + ret = issei_dmam_setup(idev); + if (ret) + return ret; + + idev->ops->irq_clear(idev); + + ret = idev->ops->hw_config(idev); + if (ret) + return ret; + + idev->process_thread = kthread_run(issei_process_thread, idev, + "kisseiprocess/%s", dev_name(&idev->dev)); + if (IS_ERR(idev->process_thread)) { + ret = PTR_ERR(idev->process_thread); + dev_err(&idev->dev, "unable to create process thread. ret = %d\n", ret); + return ret; + } + + issei_poke_process_thread(idev); + return 0; +} +EXPORT_SYMBOL_GPL(issei_start); + +/** + * issei_stop - stop interrupts and processing thread. + * @idev: the device structure + */ +void issei_stop(struct issei_device *idev) +{ + idev->power_down = true; + + idev->ops->irq_clear(idev); + idev->ops->irq_sync(idev); + + issei_poke_process_thread(idev); + + wait_event_timeout(idev->wait_rst_state, + (idev->rst_state == ISSEI_RST_STATE_DISABLED), + msecs_to_jiffies(ISSEI_STOP_TIMEOUT_MSEC)); + kthread_stop(idev->process_thread); +} +EXPORT_SYMBOL_GPL(issei_stop); diff --git a/drivers/misc/issei/pci_heci.c b/drivers/misc/issei/pci_heci.c new file mode 100644 index 000000000000..86c567a4bf0f --- /dev/null +++ b/drivers/misc/issei/pci_heci.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pm.h> + +#include "cdev.h" +#include "hw_heci.h" +#include "hw_heci_regs.h" + +static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + const struct hw_heci_cfg *cfg; + struct issei_device *idev; + struct issei_heci_hw *hw; + char __iomem *registers; + int err; + + cfg = issei_heci_get_cfg(ent->driver_data); + if (!cfg) + return dev_err_probe(dev, -ENODEV, "no usable configuration.\n"); + + err = pcim_enable_device(pdev); + if (err) + return dev_err_probe(dev, err, "failed to enable pci device.\n"); + + pci_set_master(pdev); + + registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME); + if (IS_ERR(registers)) + return dev_err_probe(dev, PTR_ERR(registers), "failed to get pci region.\n"); + + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (err) + return dev_err_probe(dev, err, "no usable DMA configuration.\n"); + + idev = issei_register(sizeof(*hw), dev, &cfg->dma_length, issei_heci_get_ops()); + if (IS_ERR(idev)) + return dev_err_probe(dev, PTR_ERR(idev), "register failure.\n"); + + issei_heci_dev_init(idev, registers, cfg); + + pci_set_drvdata(pdev, idev); + + err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (err < 0) { + dev_err_probe(dev, err, "pci_alloc_irq_vectors failure.\n"); + goto deregister; + } + + hw = to_heci_hw(idev); + hw->irq = pci_irq_vector(pdev, 0); + + err = request_threaded_irq(hw->irq, + issei_heci_irq_quick_handler, + NULL, + IRQF_SHARED, KBUILD_MODNAME, idev); + if (err) + goto release_irq; + + err = issei_start(idev); + if (err) { + dev_err_probe(dev, err, "init hw failure.\n"); + goto free_irq; + } + + return 0; + +free_irq: + idev->ops->irq_disable(idev); + free_irq(hw->irq, idev); +release_irq: + pci_free_irq_vectors(pdev); +deregister: + issei_deregister(idev); + return err; +} + +static void issei_heci_shutdown(struct pci_dev *pdev) +{ + struct issei_device *idev = pci_get_drvdata(pdev); + struct issei_heci_hw *hw = to_heci_hw(idev); + + issei_stop(idev); + + idev->ops->irq_disable(idev); + free_irq(hw->irq, idev); + pci_free_irq_vectors(pdev); +} + +static void issei_heci_remove(struct pci_dev *pdev) +{ + issei_heci_shutdown(pdev); + + issei_deregister(pci_get_drvdata(pdev)); +} + +static int issei_heci_pm_suspend(struct device *device) +{ + struct issei_device *idev = dev_get_drvdata(device); + + issei_stop(idev); + idev->ops->irq_disable(idev); + + return 0; +} + +static int issei_heci_pm_resume(struct device *device) +{ + struct issei_device *idev = dev_get_drvdata(device); + + return issei_start(idev); +} + +static const struct dev_pm_ops issei_heci_pm_ops = { + SYSTEM_SLEEP_PM_OPS(issei_heci_pm_suspend, issei_heci_pm_resume) +}; + +static const struct pci_device_id heci_pci_tbl[] = { + {PCI_VDEVICE(INTEL, 0xA85D)}, /* Lunar Lake M */ + {PCI_VDEVICE(INTEL, 0xE35D)}, /* Panther Lake H */ + {PCI_VDEVICE(INTEL, 0xE45D)}, /* Panther Lake P */ + {PCI_VDEVICE(INTEL, 0xD470)}, /* Nova Lake S */ + {PCI_VDEVICE(INTEL, 0xD358)}, /* Nova Lake H */ + {PCI_VDEVICE(INTEL, 0x4D5D)}, /* Wildcat Lake */ + {} +}; +MODULE_DEVICE_TABLE(pci, heci_pci_tbl); + +static struct pci_driver issei_heci_driver = { + .name = KBUILD_MODNAME, + .id_table = heci_pci_tbl, + .probe = issei_heci_probe, + .remove = issei_heci_remove, + .shutdown = issei_heci_shutdown, + .driver = { + .pm = &issei_heci_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_pci_driver(issei_heci_driver); + +MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface - HECI"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("INTEL_SSEI"); diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index 6c65fbf22e75..c2ca63e40814 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -887,7 +887,7 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) else return -ENODEV; - ret = pci_enable_device(pci_dev); + ret = pcim_enable_device(pci_dev); if (ret) return ret; pci_set_master(pci_dev); @@ -896,18 +896,15 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) startup.end = startup.start + cp500->devs->startup.size - 1; cp500->system_startup_addr = devm_ioremap_resource(&pci_dev->dev, &startup); - if (IS_ERR(cp500->system_startup_addr)) { - ret = PTR_ERR(cp500->system_startup_addr); - goto out_disable; - } + if (IS_ERR(cp500->system_startup_addr)) + return PTR_ERR(cp500->system_startup_addr); cp500->msix_num = pci_alloc_irq_vectors(pci_dev, CP500_NUM_MSIX_NO_MMI, CP500_NUM_MSIX, PCI_IRQ_MSIX); if (cp500->msix_num < CP500_NUM_MSIX_NO_MMI) { dev_err(&pci_dev->dev, "Hardware does not support enough MSI-X interrupts\n"); - ret = -ENODEV; - goto out_disable; + return -ENODEV; } cp500_vers = ioread32(cp500->system_startup_addr + CP500_VERSION_REG); @@ -937,9 +934,6 @@ out_unregister_nvmem: nvmem_unregister_notifier(&cp500->nvmem_notifier); out_free_irq: pci_free_irq_vectors(pci_dev); -out_disable: - pci_clear_master(pci_dev); - pci_disable_device(pci_dev); return ret; } @@ -962,9 +956,6 @@ static void cp500_remove(struct pci_dev *pci_dev) pci_set_drvdata(pci_dev, 0); pci_free_irq_vectors(pci_dev); - - pci_clear_master(pci_dev); - pci_disable_device(pci_dev); } static struct pci_device_id cp500_ids[] = { diff --git a/drivers/misc/keba/lan9252.c b/drivers/misc/keba/lan9252.c index fc54afd1d05b..874dd50fac66 100644 --- a/drivers/misc/keba/lan9252.c +++ b/drivers/misc/keba/lan9252.c @@ -339,8 +339,8 @@ out: } static const struct spi_device_id lan9252_id[] = { - {"lan9252"}, - {} + { .name = "lan9252" }, + { } }; MODULE_DEVICE_TABLE(spi, lan9252_id); diff --git a/drivers/misc/lan966x_pci.c b/drivers/misc/lan966x_pci.c index 0bb90c0943bf..b0949c653e5b 100644 --- a/drivers/misc/lan966x_pci.c +++ b/drivers/misc/lan966x_pci.c @@ -183,6 +183,7 @@ static int lan966x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i return 0; err_unload_overlay: + of_platform_depopulate(dev); lan966x_pci_unload_overlay(data); return ret; } diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 93949df3bcff..740b5cf53842 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -219,8 +219,8 @@ static void lattice_ecp3_remove(struct spi_device *spi) } static const struct spi_device_id lattice_ecp3_id[] = { - { "ecp3-17", 0 }, - { "ecp3-35", 0 }, + { .name = "ecp3-17" }, + { .name = "ecp3-35" }, { } }; MODULE_DEVICE_TABLE(spi, lattice_ecp3_id); diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index 15119584473c..7c3ca6d604d1 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -95,7 +95,7 @@ static union axis_conversion lis3lv02d_axis_map = #ifdef CONFIG_OF static const struct of_device_id lis3lv02d_i2c_dt_ids[] = { { .compatible = "st,lis3lv02d" }, - {} + { } }; MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids); #endif @@ -247,9 +247,9 @@ static int lis3_i2c_runtime_resume(struct device *dev) #endif /* CONFIG_PM */ static const struct i2c_device_id lis3lv02d_id[] = { - {"lis3lv02d", LIS3LV02D}, - {"lis331dlh", LIS331DLH}, - {} + { .name = "lis3lv02d", .driver_data = LIS3LV02D }, + { .name = "lis331dlh", .driver_data = LIS331DLH }, + { } }; MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c index 203a108b8883..5bf95176a00e 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c @@ -60,7 +60,7 @@ static union axis_conversion lis3lv02d_axis_normal = #ifdef CONFIG_OF static const struct of_device_id lis302dl_spi_dt_ids[] = { { .compatible = "st,lis302dl-spi" }, - {} + { } }; MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids); #endif diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c index 48bea1117771..b0b678e6b28d 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c @@ -150,7 +150,7 @@ static const struct pci_device_id pci1xxxx_tbl[] = { { PCI_DEVICE(0x1055, 0xA035) }, { PCI_DEVICE(0x1055, 0xA045) }, { PCI_DEVICE(0x1055, 0xA055) }, - {0,} + { } }; MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl); diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 8eddbaa1fccd..82635db04dc5 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -513,8 +513,8 @@ static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev, static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_gpio_pm_ops, pci1xxxx_gpio_suspend, pci1xxxx_gpio_resume); static const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { - {.name = "mchp_pci1xxxx_gp.gp_gpio"}, - {} + { .name = "mchp_pci1xxxx_gp.gp_gpio" }, + { } }; MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_gpio_auxiliary_id_table); diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c index a2ed477e0370..09841c4f1a06 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c @@ -419,8 +419,8 @@ static void pci1xxxx_otp_eeprom_remove(struct auxiliary_device *aux_dev) } static const struct auxiliary_device_id pci1xxxx_otp_eeprom_auxiliary_id_table[] = { - {.name = "mchp_pci1xxxx_gp.gp_otp_e2p"}, - {}, + { .name = "mchp_pci1xxxx_gp.gp_otp_e2p" }, + { } }; MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_eeprom_auxiliary_id_table); diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c index 73d5beeb9c34..376840b2c354 100644 --- a/drivers/misc/mei/gsc-me.c +++ b/drivers/misc/mei/gsc-me.c @@ -101,10 +101,8 @@ static int mei_gsc_probe(struct auxiliary_device *aux_dev, mei_me_irq_quick_handler, mei_me_irq_thread_handler, IRQF_ONESHOT, KBUILD_MODNAME, dev); - if (ret) { - dev_err(device, "irq register failed %d\n", ret); + if (ret) goto err; - } } ret = mei_register(dev, device); diff --git a/drivers/misc/mei/mei_lb.c b/drivers/misc/mei/mei_lb.c index f6a258c2b838..9fa69acf28d5 100644 --- a/drivers/misc/mei/mei_lb.c +++ b/drivers/misc/mei/mei_lb.c @@ -281,7 +281,7 @@ static int mei_lb_check_response_v2(const struct device *dev, ssize_t bytes, bytes, sizeof(rsp->rheader)); return -ENOMSG; } - if (rsp->rheader.header.command_id != MEI_LB2_CMD) { + if (rsp->rheader.header.command_id != cpu_to_le32(MEI_LB2_CMD)) { dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n", rsp->rheader.header.command_id, MEI_LB2_CMD); return -EPROTO; @@ -327,7 +327,7 @@ static int mei_lb_push_payload_v2(struct device *dev, struct mei_cl_device *clde if (sent_data + chunk_size == payload_size) last_chunk = MEI_LB2_FLAG_LST_CHUNK; - req->header.command_id = MEI_LB2_CMD; + req->header.command_id = cpu_to_le32(MEI_LB2_CMD); req->type = cpu_to_le32(type); req->flags = cpu_to_le32(flags | first_chunk | last_chunk); req->reserved = 0; diff --git a/drivers/misc/mei/pci-csc.c b/drivers/misc/mei/pci-csc.c index 70792bf9b3c0..6e1e8008f267 100644 --- a/drivers/misc/mei/pci-csc.c +++ b/drivers/misc/mei/pci-csc.c @@ -240,7 +240,7 @@ static const struct dev_pm_ops mei_csc_pm_ops = { static const struct pci_device_id mei_csc_pci_tbl[] = { { PCI_DEVICE_DATA(INTEL, MEI_CRI, MEI_ME_CSC_CFG) }, - {} + { } }; MODULE_DEVICE_TABLE(pci, mei_csc_pci_tbl); diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 55e0b8a98827..790a4647811b 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -27,114 +27,114 @@ /* mei_pci_tbl - PCI Device ID Table */ static const struct pci_device_id mei_me_pci_tbl[] = { - {PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG)}, - - {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG) }, + + { PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG) }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl); diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index f5441bc5efe2..815ca72877ce 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -24,10 +24,10 @@ #include "hw-txe.h" static const struct pci_device_id mei_txe_pci_tbl[] = { - {PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */ - {PCI_VDEVICE(INTEL, 0x2298)}, /* Cherrytrail */ + { PCI_VDEVICE(INTEL, 0x0F18) }, /* Baytrail */ + { PCI_VDEVICE(INTEL, 0x2298) }, /* Cherrytrail */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl); diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c index 5100234ba5b6..ef6dcc043862 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c @@ -435,7 +435,7 @@ static int mei_vsc_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume); static const struct platform_device_id mei_vsc_id_table[] = { - { MEI_VSC_DRV_NAME }, + { .name = MEI_VSC_DRV_NAME }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mei_vsc_id_table); diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c index 5ecf99883996..432bc59f11f9 100644 --- a/drivers/misc/mei/vsc-tp.c +++ b/drivers/misc/mei/vsc-tp.c @@ -552,11 +552,11 @@ static void vsc_tp_remove(struct spi_device *spi) } static const struct acpi_device_id vsc_tp_acpi_ids[] = { - { "INTC1009" }, /* Raptor Lake */ - { "INTC1058" }, /* Tiger Lake */ - { "INTC1094" }, /* Alder Lake */ - { "INTC10D0" }, /* Meteor Lake */ - {} + { .id = "INTC1009" }, /* Raptor Lake */ + { .id = "INTC1058" }, /* Tiger Lake */ + { .id = "INTC1094" }, /* Alder Lake */ + { .id = "INTC10D0" }, /* Meteor Lake */ + { } }; MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids); diff --git a/drivers/misc/mrvl_cn10k_dpi.c b/drivers/misc/mrvl_cn10k_dpi.c index 7d5433121ff6..73ff0f198ec3 100644 --- a/drivers/misc/mrvl_cn10k_dpi.c +++ b/drivers/misc/mrvl_cn10k_dpi.c @@ -470,10 +470,8 @@ static int dpi_irq_init(struct dpipf *dpi) ret = devm_request_irq(dev, pci_irq_vector(pdev, DPI_MBOX_PF_VF_INT_IDX), dpi_mbox_intr_handler, 0, "dpi-mbox", dpi); - if (ret) { - dev_err(dev, "DPI: request_irq failed for mbox; err=%d\n", ret); + if (ret) return ret; - } dpi_reg_write(dpi, DPI_MBOX_VF_PF_INT_ENA_W1S, GENMASK_ULL(31, 0)); @@ -658,7 +656,7 @@ static void dpi_remove(struct pci_dev *pdev) static const struct pci_device_id dpi_id_table[] = { { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_MRVL_CN10K_DPI_PF, PCI_VENDOR_ID_CAVIUM, PCI_SUBDEVID_MRVL_CN10K_DPI_PF) }, - { 0, } /* end of table */ + { } /* end of table */ }; static struct pci_driver dpi_driver = { diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index 3960506eb7ab..a3a9f9bfc6b7 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -61,7 +61,7 @@ struct nsm { /* NSM device ID */ static const struct virtio_device_id id_table[] = { { VIRTIO_ID_NITRO_SEC_MOD, VIRTIO_DEV_ANY_ID }, - { 0 }, + { } }; static struct nsm *file_to_nsm(struct file *file) @@ -243,7 +243,7 @@ static int nsm_sendrecv_msg_locked(struct nsm *nsm) goto cleanup; } - msg->resp.len = len; + msg->resp.len = min_t(unsigned int, len, sizeof(msg->resp.data)); rc = 0; @@ -365,9 +365,8 @@ static long nsm_dev_ioctl(struct file *file, unsigned int cmd, return -EINVAL; /* Copy user argument struct to kernel argument struct */ - r = -EFAULT; if (copy_from_user(&raw, argp, _IOC_SIZE(cmd))) - return r; + return -EFAULT; mutex_lock(&nsm->lock); diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c index 02c9d1192812..4a805919bb0c 100644 --- a/drivers/misc/ntsync.c +++ b/drivers/misc/ntsync.c @@ -875,6 +875,9 @@ static int setup_wait(struct ntsync_device *dev, if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME)) return -EINVAL; + if (!args->owner) + return -EINVAL; + if (size >= sizeof(fds)) return -EINVAL; diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c index cb920aa88d3a..0cf38bd482a9 100644 --- a/drivers/misc/ocxl/pci.c +++ b/drivers/misc/ocxl/pci.c @@ -9,7 +9,7 @@ * vendor/device ID to help differentiate devices. */ static const struct pci_device_id ocxl_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), }, + { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B) }, { } }; MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl); diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c index 45060fb4ea27..b3cf59954111 100644 --- a/drivers/misc/open-dice.c +++ b/drivers/misc/open-dice.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2021 - Google LLC * Author: David Brazdil <dbrazdil@google.com> + * Author: Song Guo <songguo@google.com> * * Driver for Open Profile for DICE. * @@ -19,6 +20,7 @@ * close(fd); */ +#include <linux/acpi.h> #include <linux/io.h> #include <linux/miscdevice.h> #include <linux/mm.h> @@ -31,7 +33,8 @@ struct open_dice_drvdata { struct mutex lock; char name[16]; - struct reserved_mem *rmem; + phys_addr_t mem_base; + resource_size_t mem_size; struct miscdevice misc; }; @@ -45,14 +48,14 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata) void *kaddr; mutex_lock(&drvdata->lock); - kaddr = devm_memremap(drvdata->misc.this_device, drvdata->rmem->base, - drvdata->rmem->size, MEMREMAP_WC); + kaddr = devm_memremap(drvdata->misc.this_device, drvdata->mem_base, + drvdata->mem_size, MEMREMAP_WC); if (IS_ERR(kaddr)) { mutex_unlock(&drvdata->lock); return PTR_ERR(kaddr); } - memset(kaddr, 0, drvdata->rmem->size); + memset(kaddr, 0, drvdata->mem_size); devm_memunmap(drvdata->misc.this_device, kaddr); mutex_unlock(&drvdata->lock); return 0; @@ -64,7 +67,7 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata) static ssize_t open_dice_read(struct file *filp, char __user *ptr, size_t len, loff_t *off) { - unsigned long val = to_open_dice_drvdata(filp)->rmem->size; + unsigned long val = to_open_dice_drvdata(filp)->mem_size; return simple_read_from_buffer(ptr, len, off, &val, sizeof(val)); } @@ -102,8 +105,8 @@ static int open_dice_mmap_prepare(struct vm_area_desc *desc) /* Create write-combine mapping so all clients observe a wipe. */ desc->page_prot = pgprot_writecombine(desc->page_prot); vma_desc_set_flags(desc, VMA_DONTCOPY_BIT, VMA_DONTDUMP_BIT); - mmap_action_simple_ioremap(desc, drvdata->rmem->base, - drvdata->rmem->size); + mmap_action_simple_ioremap(desc, drvdata->mem_base, + drvdata->mem_size); return 0; } @@ -118,22 +121,40 @@ static int __init open_dice_probe(struct platform_device *pdev) { static unsigned int dev_idx; struct device *dev = &pdev->dev; - struct reserved_mem *rmem; struct open_dice_drvdata *drvdata; + phys_addr_t mem_base; + resource_size_t mem_size; int ret; - rmem = of_reserved_mem_lookup(dev->of_node); - if (!rmem) { - dev_err(dev, "failed to lookup reserved memory\n"); + if (dev->of_node) { + struct reserved_mem *rmem = of_reserved_mem_lookup(dev->of_node); + + if (!rmem) { + dev_err(dev, "failed to lookup reserved memory\n"); + return -EINVAL; + } + mem_base = rmem->base; + mem_size = rmem->size; + } else if (is_acpi_node(dev->fwnode)) { + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(dev, "failed to get MMIO resource\n"); + return -EINVAL; + } + mem_base = res->start; + mem_size = resource_size(res); + } else { + dev_err(dev, "device not supported (no DT or ACPI node)\n"); return -EINVAL; } - if (!rmem->size || (rmem->size > ULONG_MAX)) { + if (!mem_size || (mem_size > ULONG_MAX)) { dev_err(dev, "invalid memory region size\n"); return -EINVAL; } - if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) { + if (!PAGE_ALIGNED(mem_base) || !PAGE_ALIGNED(mem_size)) { dev_err(dev, "memory region must be page-aligned\n"); return -EINVAL; } @@ -143,7 +164,8 @@ static int __init open_dice_probe(struct platform_device *pdev) return -ENOMEM; *drvdata = (struct open_dice_drvdata){ - .rmem = rmem, + .mem_base = mem_base, + .mem_size = mem_size, .misc = (struct miscdevice){ .parent = dev, .name = drvdata->name, @@ -177,7 +199,7 @@ static void open_dice_remove(struct platform_device *pdev) static const struct of_device_id open_dice_of_match[] = { { .compatible = "google,open-dice" }, - {}, + { } }; static struct platform_driver open_dice_driver = { @@ -207,3 +229,4 @@ module_exit(open_dice_exit); MODULE_DESCRIPTION("Driver for Open Profile for DICE."); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>"); +MODULE_AUTHOR("Song Guo <songguo@google.com>"); diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 19c4fa017f24..15785597da40 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -83,22 +83,16 @@ #define PCH_PHUB_OROM_SIZE 15360 +enum pch_phub_type { + PCH_EG20T, + PCH_ML7213, + PCH_ML7223M, + PCH_ML7223N, + PCH_ML7831, +}; + /** * struct pch_phub_reg - PHUB register structure - * @phub_id_reg: PHUB_ID register val - * @q_pri_val_reg: QUEUE_PRI_VAL register val - * @rc_q_maxsize_reg: RC_QUEUE_MAXSIZE register val - * @bri_q_maxsize_reg: BRI_QUEUE_MAXSIZE register val - * @comp_resp_timeout_reg: COMP_RESP_TIMEOUT register val - * @bus_slave_control_reg: BUS_SLAVE_CONTROL_REG register val - * @deadlock_avoid_type_reg: DEADLOCK_AVOID_TYPE register val - * @intpin_reg_wpermit_reg0: INTPIN_REG_WPERMIT register 0 val - * @intpin_reg_wpermit_reg1: INTPIN_REG_WPERMIT register 1 val - * @intpin_reg_wpermit_reg2: INTPIN_REG_WPERMIT register 2 val - * @intpin_reg_wpermit_reg3: INTPIN_REG_WPERMIT register 3 val - * @int_reduce_control_reg: INT_REDUCE_CONTROL registers val - * @clkcfg_reg: CLK CFG register val - * @funcsel_reg: Function select register value * @pch_phub_base_address: Register base address * @pch_phub_extrom_base_address: external rom base address * @pch_mac_start_address: MAC address area start address @@ -107,25 +101,11 @@ * @pdev: pointer to pci device struct */ struct pch_phub_reg { - u32 phub_id_reg; - u32 q_pri_val_reg; - u32 rc_q_maxsize_reg; - u32 bri_q_maxsize_reg; - u32 comp_resp_timeout_reg; - u32 bus_slave_control_reg; - u32 deadlock_avoid_type_reg; - u32 intpin_reg_wpermit_reg0; - u32 intpin_reg_wpermit_reg1; - u32 intpin_reg_wpermit_reg2; - u32 intpin_reg_wpermit_reg3; - u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG]; - u32 clkcfg_reg; - u32 funcsel_reg; void __iomem *pch_phub_base_address; void __iomem *pch_phub_extrom_base_address; u32 pch_mac_start_address; u32 pch_opt_rom_start_address; - int ioh_type; + enum pch_phub_type ioh_type; struct pci_dev *pdev; }; @@ -344,10 +324,19 @@ static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) int retval; int i; - if ((chip->ioh_type == 1) || (chip->ioh_type == 5)) /* EG20T or ML7831*/ + switch (chip->ioh_type) { + case PCH_EG20T: + case PCH_ML7831: retval = pch_phub_gbe_serial_rom_conf(chip); - else /* ML7223 */ + break; + + case PCH_ML7213: + case PCH_ML7223M: + case PCH_ML7223N: retval = pch_phub_gbe_serial_rom_conf_mp(chip); + break; + } + if (retval) return retval; @@ -537,14 +526,6 @@ static const struct bin_attribute pch_bin_attr = { .write = pch_phub_bin_write, }; -enum { - PCH_EG20T, - PCH_ML7213, - PCH_ML7223M, - PCH_ML7223N, - PCH_ML7831, -}; - static int pch_phub_probe(struct pci_dev *pdev, const struct pci_device_id *id) { diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 7686d777a27e..6b18ea914689 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -1419,18 +1419,18 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0), .driver_data = (kernel_ulong_t)&default_data, }, - { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8),}, + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8) }, { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_LS1088A), .driver_data = (kernel_ulong_t)&default_data, }, { PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) }, { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654), - .driver_data = (kernel_ulong_t)&am654_data + .driver_data = (kernel_ulong_t)&am654_data, }, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1),}, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1),}, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0),}, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1),}, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1) }, { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A779F0), .driver_data = (kernel_ulong_t)&default_data, }, @@ -1449,8 +1449,8 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_ROCKCHIP, PCI_DEVICE_ID_ROCKCHIP_RK3588), .driver_data = (kernel_ulong_t)&rk3588_data, }, - { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP),}, - { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP),}, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP) }, { } }; MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl); diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 34a5054a6b40..331cae539290 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -481,10 +481,10 @@ static int __maybe_unused phantom_resume(struct device *dev_d) } static struct pci_device_id phantom_pci_tbl[] = { - { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050), .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 }, - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, phantom_pci_tbl); diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c index bedcda9b6ac5..724dfddf69e4 100644 --- a/drivers/misc/pvpanic/pvpanic-mmio.c +++ b/drivers/misc/pvpanic/pvpanic-mmio.c @@ -51,14 +51,14 @@ static int pvpanic_mmio_probe(struct platform_device *pdev) } static const struct of_device_id pvpanic_mmio_match[] = { - { .compatible = "qemu,pvpanic-mmio", }, - {} + { .compatible = "qemu,pvpanic-mmio" }, + { } }; MODULE_DEVICE_TABLE(of, pvpanic_mmio_match); static const struct acpi_device_id pvpanic_device_ids[] = { - { "QEMU0001", 0 }, - { "", 0 } + { .id = "QEMU0001" }, + { } }; MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids); diff --git a/drivers/misc/pvpanic/pvpanic-pci.c b/drivers/misc/pvpanic/pvpanic-pci.c index b21598a18f6d..572c6148a297 100644 --- a/drivers/misc/pvpanic/pvpanic-pci.c +++ b/drivers/misc/pvpanic/pvpanic-pci.c @@ -35,8 +35,8 @@ static int pvpanic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e } static const struct pci_device_id pvpanic_pci_id_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC)}, - {} + { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC) }, + { } }; MODULE_DEVICE_TABLE(pci, pvpanic_pci_id_tbl); diff --git a/drivers/misc/qcom-coincell.c b/drivers/misc/qcom-coincell.c index 3c57f7429147..b58bd34fc471 100644 --- a/drivers/misc/qcom-coincell.c +++ b/drivers/misc/qcom-coincell.c @@ -124,8 +124,8 @@ static int qcom_coincell_probe(struct platform_device *pdev) } static const struct of_device_id qcom_coincell_match_table[] = { - { .compatible = "qcom,pm8941-coincell", }, - {} + { .compatible = "qcom,pm8941-coincell" }, + { } }; MODULE_DEVICE_TABLE(of, qcom_coincell_match_table); diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c index 81685e3f3296..207a676913a2 100644 --- a/drivers/misc/rp1/rp1_pci.c +++ b/drivers/misc/rp1/rp1_pci.c @@ -166,6 +166,9 @@ static void rp1_unregister_interrupts(struct pci_dev *pdev) struct rp1_dev *rp1 = pci_get_drvdata(pdev); int irq, i; + for (i = 0; i < RP1_INT_END; i++) + irq_set_chained_handler_and_data(pci_irq_vector(pdev, i), NULL, NULL); + if (rp1->domain) { for (i = 0; i < RP1_INT_END; i++) { irq = irq_find_mapping(rp1->domain, i); @@ -191,13 +194,13 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!rp1_node) { dev_err(dev, "Missing of_node for device\n"); err = -EINVAL; - goto err_put_node; + goto err_out; } rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL); if (!rp1) { err = -ENOMEM; - goto err_put_node; + goto err_out; } rp1->pdev = pdev; @@ -206,21 +209,21 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_err(&pdev->dev, "Not initialized - is the firmware running?\n"); err = -EINVAL; - goto err_put_node; + goto err_out; } err = pcim_enable_device(pdev); if (err < 0) { err = dev_err_probe(&pdev->dev, err, "Enabling PCI device has failed"); - goto err_put_node; + goto err_out; } rp1->bar1 = pcim_iomap(pdev, 1, 0); if (!rp1->bar1) { dev_err(&pdev->dev, "Cannot map PCI BAR\n"); err = -EIO; - goto err_put_node; + goto err_out; } pci_set_master(pdev); @@ -230,16 +233,16 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err < 0) { err = dev_err_probe(&pdev->dev, err, "Failed to allocate MSI-X vectors\n"); - goto err_put_node; + goto err_out; } else if (err != RP1_INT_END) { dev_err(&pdev->dev, "Cannot allocate enough interrupts\n"); err = -EINVAL; - goto err_put_node; + goto err_out; } pci_set_drvdata(pdev, rp1); - rp1->domain = irq_domain_add_linear(rp1_node, RP1_INT_END, - &rp1_domain_ops, rp1); + rp1->domain = irq_domain_create_linear(of_fwnode_handle(rp1_node), RP1_INT_END, + &rp1_domain_ops, rp1); if (!rp1->domain) { dev_err(&pdev->dev, "Error creating IRQ domain\n"); err = -ENOMEM; @@ -267,15 +270,11 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_unregister_interrupts; } - of_node_put(rp1_node); - return 0; err_unregister_interrupts: rp1_unregister_interrupts(pdev); -err_put_node: - of_node_put(rp1_node); - +err_out: return err; } @@ -288,7 +287,7 @@ static void rp1_remove(struct pci_dev *pdev) } static const struct pci_device_id dev_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0), }, + { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0) }, { } }; MODULE_DEVICE_TABLE(pci, dev_id_table); diff --git a/drivers/misc/sgi-gru/Makefile b/drivers/misc/sgi-gru/Makefile deleted file mode 100644 index 8132116ec0f0..000000000000 --- a/drivers/misc/sgi-gru/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -ccflags-$(CONFIG_SGI_GRU_DEBUG) := -DDEBUG - -obj-$(CONFIG_SGI_GRU) := gru.o -gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o gruhandles.o grukdump.o - diff --git a/drivers/misc/sgi-gru/gru.h b/drivers/misc/sgi-gru/gru.h deleted file mode 100644 index 6ae045037219..000000000000 --- a/drivers/misc/sgi-gru/gru.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __GRU_H__ -#define __GRU_H__ - -/* - * GRU architectural definitions - */ -#define GRU_CACHE_LINE_BYTES 64 -#define GRU_HANDLE_STRIDE 256 -#define GRU_CB_BASE 0 -#define GRU_DS_BASE 0x20000 - -/* - * Size used to map GRU GSeg - */ -#if defined(CONFIG_X86_64) -#define GRU_GSEG_PAGESIZE (256 * 1024UL) /* ZZZ 2MB ??? */ -#else -#error "Unsupported architecture" -#endif - -/* - * Structure for obtaining GRU resource information - */ -struct gru_chiplet_info { - int node; - int chiplet; - int blade; - int total_dsr_bytes; - int total_cbr; - int total_user_dsr_bytes; - int total_user_cbr; - int free_user_dsr_bytes; - int free_user_cbr; -}; - -/* - * Statictics kept for each context. - */ -struct gru_gseg_statistics { - unsigned long fmm_tlbmiss; - unsigned long upm_tlbmiss; - unsigned long tlbdropin; - unsigned long context_stolen; - unsigned long reserved[10]; -}; - -/* Flags for GRU options on the gru_create_context() call */ -/* Select one of the follow 4 options to specify how TLB misses are handled */ -#define GRU_OPT_MISS_DEFAULT 0x0000 /* Use default mode */ -#define GRU_OPT_MISS_USER_POLL 0x0001 /* User will poll CB for faults */ -#define GRU_OPT_MISS_FMM_INTR 0x0002 /* Send interrupt to cpu to - handle fault */ -#define GRU_OPT_MISS_FMM_POLL 0x0003 /* Use system polling thread */ -#define GRU_OPT_MISS_MASK 0x0003 /* Mask for TLB MISS option */ - - - -#endif /* __GRU_H__ */ diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h deleted file mode 100644 index da5eb9edf9ec..000000000000 --- a/drivers/misc/sgi-gru/gru_instructions.h +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __GRU_INSTRUCTIONS_H__ -#define __GRU_INSTRUCTIONS_H__ - -extern int gru_check_status_proc(void *cb); -extern int gru_wait_proc(void *cb); -extern void gru_wait_abort_proc(void *cb); - - - -/* - * Architecture dependent functions - */ - -#if defined(CONFIG_X86_64) -#include <asm/cacheflush.h> -#define __flush_cache(p) clflush(p) -#define gru_ordered_store_ulong(p, v) \ - do { \ - barrier(); \ - *(unsigned long *)p = v; \ - } while (0) -#else -#error "Unsupported architecture" -#endif - -/* - * Control block status and exception codes - */ -#define CBS_IDLE 0 -#define CBS_EXCEPTION 1 -#define CBS_ACTIVE 2 -#define CBS_CALL_OS 3 - -/* CB substatus bitmasks */ -#define CBSS_MSG_QUEUE_MASK 7 -#define CBSS_IMPLICIT_ABORT_ACTIVE_MASK 8 - -/* CB substatus message queue values (low 3 bits of substatus) */ -#define CBSS_NO_ERROR 0 -#define CBSS_LB_OVERFLOWED 1 -#define CBSS_QLIMIT_REACHED 2 -#define CBSS_PAGE_OVERFLOW 3 -#define CBSS_AMO_NACKED 4 -#define CBSS_PUT_NACKED 5 - -/* - * Structure used to fetch exception detail for CBs that terminate with - * CBS_EXCEPTION - */ -struct control_block_extended_exc_detail { - unsigned long cb; - int opc; - int ecause; - int exopc; - long exceptdet0; - int exceptdet1; - int cbrstate; - int cbrexecstatus; -}; - -/* - * Instruction formats - */ - -/* - * Generic instruction format. - * This definition has precise bit field definitions. - */ -struct gru_instruction_bits { - /* DW 0 - low */ - unsigned int icmd: 1; - unsigned char ima: 3; /* CB_DelRep, unmapped mode */ - unsigned char reserved0: 4; - unsigned int xtype: 3; - unsigned int iaa0: 2; - unsigned int iaa1: 2; - unsigned char reserved1: 1; - unsigned char opc: 8; /* opcode */ - unsigned char exopc: 8; /* extended opcode */ - /* DW 0 - high */ - unsigned int idef2: 22; /* TRi0 */ - unsigned char reserved2: 2; - unsigned char istatus: 2; - unsigned char isubstatus:4; - unsigned char reserved3: 1; - unsigned char tlb_fault_color: 1; - /* DW 1 */ - unsigned long idef4; /* 42 bits: TRi1, BufSize */ - /* DW 2-6 */ - unsigned long idef1; /* BAddr0 */ - unsigned long idef5; /* Nelem */ - unsigned long idef6; /* Stride, Operand1 */ - unsigned long idef3; /* BAddr1, Value, Operand2 */ - unsigned long reserved4; - /* DW 7 */ - unsigned long avalue; /* AValue */ -}; - -/* - * Generic instruction with friendlier names. This format is used - * for inline instructions. - */ -struct gru_instruction { - /* DW 0 */ - union { - unsigned long op64; /* icmd,xtype,iaa0,ima,opc,tri0 */ - struct { - unsigned int op32; - unsigned int tri0; - }; - }; - unsigned long tri1_bufsize; /* DW 1 */ - unsigned long baddr0; /* DW 2 */ - unsigned long nelem; /* DW 3 */ - unsigned long op1_stride; /* DW 4 */ - unsigned long op2_value_baddr1; /* DW 5 */ - unsigned long reserved0; /* DW 6 */ - unsigned long avalue; /* DW 7 */ -}; - -/* Some shifts and masks for the low 64 bits of a GRU command */ -#define GRU_CB_ICMD_SHFT 0 -#define GRU_CB_ICMD_MASK 0x1 -#define GRU_CB_XTYPE_SHFT 8 -#define GRU_CB_XTYPE_MASK 0x7 -#define GRU_CB_IAA0_SHFT 11 -#define GRU_CB_IAA0_MASK 0x3 -#define GRU_CB_IAA1_SHFT 13 -#define GRU_CB_IAA1_MASK 0x3 -#define GRU_CB_IMA_SHFT 1 -#define GRU_CB_IMA_MASK 0x3 -#define GRU_CB_OPC_SHFT 16 -#define GRU_CB_OPC_MASK 0xff -#define GRU_CB_EXOPC_SHFT 24 -#define GRU_CB_EXOPC_MASK 0xff -#define GRU_IDEF2_SHFT 32 -#define GRU_IDEF2_MASK 0x3ffff -#define GRU_ISTATUS_SHFT 56 -#define GRU_ISTATUS_MASK 0x3 - -/* GRU instruction opcodes (opc field) */ -#define OP_NOP 0x00 -#define OP_BCOPY 0x01 -#define OP_VLOAD 0x02 -#define OP_IVLOAD 0x03 -#define OP_VSTORE 0x04 -#define OP_IVSTORE 0x05 -#define OP_VSET 0x06 -#define OP_IVSET 0x07 -#define OP_MESQ 0x08 -#define OP_GAMXR 0x09 -#define OP_GAMIR 0x0a -#define OP_GAMIRR 0x0b -#define OP_GAMER 0x0c -#define OP_GAMERR 0x0d -#define OP_BSTORE 0x0e -#define OP_VFLUSH 0x0f - - -/* Extended opcodes values (exopc field) */ - -/* GAMIR - AMOs with implicit operands */ -#define EOP_IR_FETCH 0x01 /* Plain fetch of memory */ -#define EOP_IR_CLR 0x02 /* Fetch and clear */ -#define EOP_IR_INC 0x05 /* Fetch and increment */ -#define EOP_IR_DEC 0x07 /* Fetch and decrement */ -#define EOP_IR_QCHK1 0x0d /* Queue check, 64 byte msg */ -#define EOP_IR_QCHK2 0x0e /* Queue check, 128 byte msg */ - -/* GAMIRR - Registered AMOs with implicit operands */ -#define EOP_IRR_FETCH 0x01 /* Registered fetch of memory */ -#define EOP_IRR_CLR 0x02 /* Registered fetch and clear */ -#define EOP_IRR_INC 0x05 /* Registered fetch and increment */ -#define EOP_IRR_DEC 0x07 /* Registered fetch and decrement */ -#define EOP_IRR_DECZ 0x0f /* Registered fetch and decrement, update on zero*/ - -/* GAMER - AMOs with explicit operands */ -#define EOP_ER_SWAP 0x00 /* Exchange argument and memory */ -#define EOP_ER_OR 0x01 /* Logical OR with memory */ -#define EOP_ER_AND 0x02 /* Logical AND with memory */ -#define EOP_ER_XOR 0x03 /* Logical XOR with memory */ -#define EOP_ER_ADD 0x04 /* Add value to memory */ -#define EOP_ER_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/ -#define EOP_ER_CADD 0x0c /* Queue check, operand1*64 byte msg */ - -/* GAMERR - Registered AMOs with explicit operands */ -#define EOP_ERR_SWAP 0x00 /* Exchange argument and memory */ -#define EOP_ERR_OR 0x01 /* Logical OR with memory */ -#define EOP_ERR_AND 0x02 /* Logical AND with memory */ -#define EOP_ERR_XOR 0x03 /* Logical XOR with memory */ -#define EOP_ERR_ADD 0x04 /* Add value to memory */ -#define EOP_ERR_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/ -#define EOP_ERR_EPOLL 0x09 /* Poll for equality */ -#define EOP_ERR_NPOLL 0x0a /* Poll for inequality */ - -/* GAMXR - SGI Arithmetic unit */ -#define EOP_XR_CSWAP 0x0b /* Masked compare exchange */ - - -/* Transfer types (xtype field) */ -#define XTYPE_B 0x0 /* byte */ -#define XTYPE_S 0x1 /* short (2-byte) */ -#define XTYPE_W 0x2 /* word (4-byte) */ -#define XTYPE_DW 0x3 /* doubleword (8-byte) */ -#define XTYPE_CL 0x6 /* cacheline (64-byte) */ - - -/* Instruction access attributes (iaa0, iaa1 fields) */ -#define IAA_RAM 0x0 /* normal cached RAM access */ -#define IAA_NCRAM 0x2 /* noncoherent RAM access */ -#define IAA_MMIO 0x1 /* noncoherent memory-mapped I/O space */ -#define IAA_REGISTER 0x3 /* memory-mapped registers, etc. */ - - -/* Instruction mode attributes (ima field) */ -#define IMA_MAPPED 0x0 /* Virtual mode */ -#define IMA_CB_DELAY 0x1 /* hold read responses until status changes */ -#define IMA_UNMAPPED 0x2 /* bypass the TLBs (OS only) */ -#define IMA_INTERRUPT 0x4 /* Interrupt when instruction completes */ - -/* CBE ecause bits */ -#define CBE_CAUSE_RI (1 << 0) -#define CBE_CAUSE_INVALID_INSTRUCTION (1 << 1) -#define CBE_CAUSE_UNMAPPED_MODE_FORBIDDEN (1 << 2) -#define CBE_CAUSE_PE_CHECK_DATA_ERROR (1 << 3) -#define CBE_CAUSE_IAA_GAA_MISMATCH (1 << 4) -#define CBE_CAUSE_DATA_SEGMENT_LIMIT_EXCEPTION (1 << 5) -#define CBE_CAUSE_OS_FATAL_TLB_FAULT (1 << 6) -#define CBE_CAUSE_EXECUTION_HW_ERROR (1 << 7) -#define CBE_CAUSE_TLBHW_ERROR (1 << 8) -#define CBE_CAUSE_RA_REQUEST_TIMEOUT (1 << 9) -#define CBE_CAUSE_HA_REQUEST_TIMEOUT (1 << 10) -#define CBE_CAUSE_RA_RESPONSE_FATAL (1 << 11) -#define CBE_CAUSE_RA_RESPONSE_NON_FATAL (1 << 12) -#define CBE_CAUSE_HA_RESPONSE_FATAL (1 << 13) -#define CBE_CAUSE_HA_RESPONSE_NON_FATAL (1 << 14) -#define CBE_CAUSE_ADDRESS_SPACE_DECODE_ERROR (1 << 15) -#define CBE_CAUSE_PROTOCOL_STATE_DATA_ERROR (1 << 16) -#define CBE_CAUSE_RA_RESPONSE_DATA_ERROR (1 << 17) -#define CBE_CAUSE_HA_RESPONSE_DATA_ERROR (1 << 18) -#define CBE_CAUSE_FORCED_ERROR (1 << 19) - -/* CBE cbrexecstatus bits */ -#define CBR_EXS_ABORT_OCC_BIT 0 -#define CBR_EXS_INT_OCC_BIT 1 -#define CBR_EXS_PENDING_BIT 2 -#define CBR_EXS_QUEUED_BIT 3 -#define CBR_EXS_TLB_INVAL_BIT 4 -#define CBR_EXS_EXCEPTION_BIT 5 -#define CBR_EXS_CB_INT_PENDING_BIT 6 - -#define CBR_EXS_ABORT_OCC (1 << CBR_EXS_ABORT_OCC_BIT) -#define CBR_EXS_INT_OCC (1 << CBR_EXS_INT_OCC_BIT) -#define CBR_EXS_PENDING (1 << CBR_EXS_PENDING_BIT) -#define CBR_EXS_QUEUED (1 << CBR_EXS_QUEUED_BIT) -#define CBR_EXS_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT) -#define CBR_EXS_EXCEPTION (1 << CBR_EXS_EXCEPTION_BIT) -#define CBR_EXS_CB_INT_PENDING (1 << CBR_EXS_CB_INT_PENDING_BIT) - -/* - * Exceptions are retried for the following cases. If any OTHER bits are set - * in ecause, the exception is not retryable. - */ -#define EXCEPTION_RETRY_BITS (CBE_CAUSE_EXECUTION_HW_ERROR | \ - CBE_CAUSE_TLBHW_ERROR | \ - CBE_CAUSE_RA_REQUEST_TIMEOUT | \ - CBE_CAUSE_RA_RESPONSE_NON_FATAL | \ - CBE_CAUSE_HA_RESPONSE_NON_FATAL | \ - CBE_CAUSE_RA_RESPONSE_DATA_ERROR | \ - CBE_CAUSE_HA_RESPONSE_DATA_ERROR \ - ) - -/* Message queue head structure */ -union gru_mesqhead { - unsigned long val; - struct { - unsigned int head; - unsigned int limit; - }; -}; - - -/* Generate the low word of a GRU instruction */ -static inline unsigned long -__opdword(unsigned char opcode, unsigned char exopc, unsigned char xtype, - unsigned char iaa0, unsigned char iaa1, - unsigned long idef2, unsigned char ima) -{ - return (1 << GRU_CB_ICMD_SHFT) | - ((unsigned long)CBS_ACTIVE << GRU_ISTATUS_SHFT) | - (idef2<< GRU_IDEF2_SHFT) | - (iaa0 << GRU_CB_IAA0_SHFT) | - (iaa1 << GRU_CB_IAA1_SHFT) | - (ima << GRU_CB_IMA_SHFT) | - (xtype << GRU_CB_XTYPE_SHFT) | - (opcode << GRU_CB_OPC_SHFT) | - (exopc << GRU_CB_EXOPC_SHFT); -} - -/* - * Architecture specific intrinsics - */ -static inline void gru_flush_cache(void *p) -{ - __flush_cache(p); -} - -/* - * Store the lower 64 bits of the command including the "start" bit. Then - * start the instruction executing. - */ -static inline void gru_start_instruction(struct gru_instruction *ins, unsigned long op64) -{ - gru_ordered_store_ulong(ins, op64); - mb(); - gru_flush_cache(ins); -} - - -/* Convert "hints" to IMA */ -#define CB_IMA(h) ((h) | IMA_UNMAPPED) - -/* Convert data segment cache line index into TRI0 / TRI1 value */ -#define GRU_DINDEX(i) ((i) * GRU_CACHE_LINE_BYTES) - -/* Inline functions for GRU instructions. - * Note: - * - nelem and stride are in elements - * - tri0/tri1 is in bytes for the beginning of the data segment. - */ -static inline void gru_vload_phys(void *cb, unsigned long gpa, - unsigned int tri0, int iaa, unsigned long hints) -{ - struct gru_instruction *ins = (struct gru_instruction *)cb; - - ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62); - ins->nelem = 1; - ins->op1_stride = 1; - gru_start_instruction(ins, __opdword(OP_VLOAD, 0, XTYPE_DW, iaa, 0, - (unsigned long)tri0, CB_IMA(hints))); -} - -static inline void gru_vstore_phys(void *cb, unsigned long gpa, - unsigned int tri0, int iaa, unsigned long hints) -{ - struct gru_instruction *ins = (struct gru_instruction *)cb; - - ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62); - ins->nelem = 1; - ins->op1_stride = 1; - gru_start_instruction(ins, __opdword(OP_VSTORE, 0, XTYPE_DW, iaa, 0, - (unsigned long)tri0, CB_IMA(hints))); -} - -static inline void gru_vload(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned char xtype, unsigned long nelem, - unsigned long stride, unsigned long hints) -{ - struct gru_instruction *ins = (struct gru_instruction *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->op1_stride = stride; - gru_start_instruction(ins, __opdword(OP_VLOAD, 0, xtype, IAA_RAM, 0, - (unsigned long)tri0, CB_IMA(hints))); -} - -static inline void gru_vstore(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned char xtype, unsigned long nelem, - unsigned long stride, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->op1_stride = stride; - gru_start_instruction(ins, __opdword(OP_VSTORE, 0, xtype, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline void gru_ivload(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned int tri1, unsigned char xtype, - unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->tri1_bufsize = tri1; - gru_start_instruction(ins, __opdword(OP_IVLOAD, 0, xtype, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline void gru_ivstore(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned int tri1, - unsigned char xtype, unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->tri1_bufsize = tri1; - gru_start_instruction(ins, __opdword(OP_IVSTORE, 0, xtype, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline void gru_vset(void *cb, unsigned long mem_addr, - unsigned long value, unsigned char xtype, unsigned long nelem, - unsigned long stride, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->op2_value_baddr1 = value; - ins->nelem = nelem; - ins->op1_stride = stride; - gru_start_instruction(ins, __opdword(OP_VSET, 0, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_ivset(void *cb, unsigned long mem_addr, - unsigned int tri1, unsigned long value, unsigned char xtype, - unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->op2_value_baddr1 = value; - ins->nelem = nelem; - ins->tri1_bufsize = tri1; - gru_start_instruction(ins, __opdword(OP_IVSET, 0, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_vflush(void *cb, unsigned long mem_addr, - unsigned long nelem, unsigned char xtype, unsigned long stride, - unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->op1_stride = stride; - ins->nelem = nelem; - gru_start_instruction(ins, __opdword(OP_VFLUSH, 0, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_nop(void *cb, int hints) -{ - struct gru_instruction *ins = (void *)cb; - - gru_start_instruction(ins, __opdword(OP_NOP, 0, 0, 0, 0, 0, CB_IMA(hints))); -} - - -static inline void gru_bcopy(void *cb, const unsigned long src, - unsigned long dest, - unsigned int tri0, unsigned int xtype, unsigned long nelem, - unsigned int bufsize, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op2_value_baddr1 = (long)dest; - ins->nelem = nelem; - ins->tri1_bufsize = bufsize; - gru_start_instruction(ins, __opdword(OP_BCOPY, 0, xtype, IAA_RAM, - IAA_RAM, tri0, CB_IMA(hints))); -} - -static inline void gru_bstore(void *cb, const unsigned long src, - unsigned long dest, unsigned int tri0, unsigned int xtype, - unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op2_value_baddr1 = (long)dest; - ins->nelem = nelem; - gru_start_instruction(ins, __opdword(OP_BSTORE, 0, xtype, 0, IAA_RAM, - tri0, CB_IMA(hints))); -} - -static inline void gru_gamir(void *cb, int exopc, unsigned long src, - unsigned int xtype, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - gru_start_instruction(ins, __opdword(OP_GAMIR, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamirr(void *cb, int exopc, unsigned long src, - unsigned int xtype, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - gru_start_instruction(ins, __opdword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamer(void *cb, int exopc, unsigned long src, - unsigned int xtype, - unsigned long operand1, unsigned long operand2, - unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op1_stride = operand1; - ins->op2_value_baddr1 = operand2; - gru_start_instruction(ins, __opdword(OP_GAMER, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamerr(void *cb, int exopc, unsigned long src, - unsigned int xtype, unsigned long operand1, - unsigned long operand2, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op1_stride = operand1; - ins->op2_value_baddr1 = operand2; - gru_start_instruction(ins, __opdword(OP_GAMERR, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamxr(void *cb, unsigned long src, - unsigned int tri0, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->nelem = 4; - gru_start_instruction(ins, __opdword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW, - IAA_RAM, 0, 0, CB_IMA(hints))); -} - -static inline void gru_mesq(void *cb, unsigned long queue, - unsigned long tri0, unsigned long nelem, - unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)queue; - ins->nelem = nelem; - gru_start_instruction(ins, __opdword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline unsigned long gru_get_amo_value(void *cb) -{ - struct gru_instruction *ins = (void *)cb; - - return ins->avalue; -} - -static inline int gru_get_amo_value_head(void *cb) -{ - struct gru_instruction *ins = (void *)cb; - - return ins->avalue & 0xffffffff; -} - -static inline int gru_get_amo_value_limit(void *cb) -{ - struct gru_instruction *ins = (void *)cb; - - return ins->avalue >> 32; -} - -static inline union gru_mesqhead gru_mesq_head(int head, int limit) -{ - union gru_mesqhead mqh; - - mqh.head = head; - mqh.limit = limit; - return mqh; -} - -/* - * Get struct control_block_extended_exc_detail for CB. - */ -extern int gru_get_cb_exception_detail(void *cb, - struct control_block_extended_exc_detail *excdet); - -#define GRU_EXC_STR_SIZE 256 - - -/* - * Control block definition for checking status - */ -struct gru_control_block_status { - unsigned int icmd :1; - unsigned int ima :3; - unsigned int reserved0 :4; - unsigned int unused1 :24; - unsigned int unused2 :24; - unsigned int istatus :2; - unsigned int isubstatus :4; - unsigned int unused3 :2; -}; - -/* Get CB status */ -static inline int gru_get_cb_status(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - - return cbs->istatus; -} - -/* Get CB message queue substatus */ -static inline int gru_get_cb_message_queue_substatus(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - - return cbs->isubstatus & CBSS_MSG_QUEUE_MASK; -} - -/* Get CB substatus */ -static inline int gru_get_cb_substatus(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - - return cbs->isubstatus; -} - -/* - * User interface to check an instruction status. UPM and exceptions - * are handled automatically. However, this function does NOT wait - * for an active instruction to complete. - * - */ -static inline int gru_check_status(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - int ret; - - ret = cbs->istatus; - if (ret != CBS_ACTIVE) - ret = gru_check_status_proc(cb); - return ret; -} - -/* - * User interface (via inline function) to wait for an instruction - * to complete. Completion status (IDLE or EXCEPTION is returned - * to the user. Exception due to hardware errors are automatically - * retried before returning an exception. - * - */ -static inline int gru_wait(void *cb) -{ - return gru_wait_proc(cb); -} - -/* - * Wait for CB to complete. Aborts program if error. (Note: error does NOT - * mean TLB mis - only fatal errors such as memory parity error or user - * bugs will cause termination. - */ -static inline void gru_wait_abort(void *cb) -{ - gru_wait_abort_proc(cb); -} - -/* - * Get a pointer to the start of a gseg - * p - Any valid pointer within the gseg - */ -static inline void *gru_get_gseg_pointer (void *p) -{ - return (void *)((unsigned long)p & ~(GRU_GSEG_PAGESIZE - 1)); -} - -/* - * Get a pointer to a control block - * gseg - GSeg address returned from gru_get_thread_gru_segment() - * index - index of desired CB - */ -static inline void *gru_get_cb_pointer(void *gseg, - int index) -{ - return gseg + GRU_CB_BASE + index * GRU_HANDLE_STRIDE; -} - -/* - * Get a pointer to a cacheline in the data segment portion of a GSeg - * gseg - GSeg address returned from gru_get_thread_gru_segment() - * index - index of desired cache line - */ -static inline void *gru_get_data_pointer(void *gseg, int index) -{ - return gseg + GRU_DS_BASE + index * GRU_CACHE_LINE_BYTES; -} - -/* - * Convert a vaddr into the tri index within the GSEG - * vaddr - virtual address of within gseg - */ -static inline int gru_get_tri(void *vaddr) -{ - return ((unsigned long)vaddr & (GRU_GSEG_PAGESIZE - 1)) - GRU_DS_BASE; -} -#endif /* __GRU_INSTRUCTIONS_H__ */ diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c deleted file mode 100644 index 3557d78ee47a..000000000000 --- a/drivers/misc/sgi-gru/grufault.c +++ /dev/null @@ -1,903 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * FAULT HANDLER FOR GRU DETECTED TLB MISSES - * - * This file contains code that handles TLB misses within the GRU. - * These misses are reported either via interrupts or user polling of - * the user CB. - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/spinlock.h> -#include <linux/mm.h> -#include <linux/hugetlb.h> -#include <linux/device.h> -#include <linux/io.h> -#include <linux/uaccess.h> -#include <linux/security.h> -#include <linux/sync_core.h> -#include <linux/prefetch.h> -#include "gru.h" -#include "grutables.h" -#include "grulib.h" -#include "gru_instructions.h" -#include <asm/uv/uv_hub.h> - -/* Return codes for vtop functions */ -#define VTOP_SUCCESS 0 -#define VTOP_INVALID -1 -#define VTOP_RETRY -2 - - -/* - * Test if a physical address is a valid GRU GSEG address - */ -static inline int is_gru_paddr(unsigned long paddr) -{ - return paddr >= gru_start_paddr && paddr < gru_end_paddr; -} - -/* - * Find the vma of a GRU segment. Caller must hold mmap_lock. - */ -struct vm_area_struct *gru_find_vma(unsigned long vaddr) -{ - struct vm_area_struct *vma; - - vma = vma_lookup(current->mm, vaddr); - if (vma && vma->vm_ops == &gru_vm_ops) - return vma; - return NULL; -} - -/* - * Find and lock the gts that contains the specified user vaddr. - * - * Returns: - * - *gts with the mmap_lock locked for read and the GTS locked. - * - NULL if vaddr invalid OR is not a valid GSEG vaddr. - */ - -static struct gru_thread_state *gru_find_lock_gts(unsigned long vaddr) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - struct gru_thread_state *gts = NULL; - - mmap_read_lock(mm); - vma = gru_find_vma(vaddr); - if (vma) - gts = gru_find_thread_state(vma, TSID(vaddr, vma)); - if (gts) - mutex_lock(>s->ts_ctxlock); - else - mmap_read_unlock(mm); - return gts; -} - -static struct gru_thread_state *gru_alloc_locked_gts(unsigned long vaddr) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - struct gru_thread_state *gts = ERR_PTR(-EINVAL); - - mmap_write_lock(mm); - vma = gru_find_vma(vaddr); - if (!vma) - goto err; - - gts = gru_alloc_thread_state(vma, TSID(vaddr, vma)); - if (IS_ERR(gts)) - goto err; - mutex_lock(>s->ts_ctxlock); - mmap_write_downgrade(mm); - return gts; - -err: - mmap_write_unlock(mm); - return gts; -} - -/* - * Unlock a GTS that was previously locked with gru_find_lock_gts(). - */ -static void gru_unlock_gts(struct gru_thread_state *gts) -{ - mutex_unlock(>s->ts_ctxlock); - mmap_read_unlock(current->mm); -} - -/* - * Set a CB.istatus to active using a user virtual address. This must be done - * just prior to a TFH RESTART. The new cb.istatus is an in-cache status ONLY. - * If the line is evicted, the status may be lost. The in-cache update - * is necessary to prevent the user from seeing a stale cb.istatus that will - * change as soon as the TFH restart is complete. Races may cause an - * occasional failure to clear the cb.istatus, but that is ok. - */ -static void gru_cb_set_istatus_active(struct gru_instruction_bits *cbk) -{ - if (cbk) { - cbk->istatus = CBS_ACTIVE; - } -} - -/* - * Read & clear a TFM - * - * The GRU has an array of fault maps. A map is private to a cpu - * Only one cpu will be accessing a cpu's fault map. - * - * This function scans the cpu-private fault map & clears all bits that - * are set. The function returns a bitmap that indicates the bits that - * were cleared. Note that sense the maps may be updated asynchronously by - * the GRU, atomic operations must be used to clear bits. - */ -static void get_clear_fault_map(struct gru_state *gru, - struct gru_tlb_fault_map *imap, - struct gru_tlb_fault_map *dmap) -{ - unsigned long i, k; - struct gru_tlb_fault_map *tfm; - - tfm = get_tfm_for_cpu(gru, gru_cpu_fault_map_id()); - prefetchw(tfm); /* Helps on hardware, required for emulator */ - for (i = 0; i < BITS_TO_LONGS(GRU_NUM_CBE); i++) { - k = tfm->fault_bits[i]; - if (k) - k = xchg(&tfm->fault_bits[i], 0UL); - imap->fault_bits[i] = k; - k = tfm->done_bits[i]; - if (k) - k = xchg(&tfm->done_bits[i], 0UL); - dmap->fault_bits[i] = k; - } - - /* - * Not functionally required but helps performance. (Required - * on emulator) - */ - gru_flush_cache(tfm); -} - -/* - * Atomic (interrupt context) & non-atomic (user context) functions to - * convert a vaddr into a physical address. The size of the page - * is returned in pageshift. - * returns: - * 0 - successful - * < 0 - error code - * 1 - (atomic only) try again in non-atomic context - */ -static int non_atomic_pte_lookup(struct vm_area_struct *vma, - unsigned long vaddr, int write, - unsigned long *paddr, int *pageshift) -{ - struct page *page; - -#ifdef CONFIG_HUGETLB_PAGE - *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT; -#else - *pageshift = PAGE_SHIFT; -#endif - if (get_user_pages(vaddr, 1, write ? FOLL_WRITE : 0, &page) <= 0) - return -EFAULT; - *paddr = page_to_phys(page); - put_page(page); - return 0; -} - -/* - * atomic_pte_lookup - * - * Convert a user virtual address to a physical address - * Only supports Intel large pages (2MB only) on x86_64. - * ZZZ - hugepage support is incomplete - * - * NOTE: mmap_lock is already held on entry to this function. This - * guarantees existence of the page tables. - */ -static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr, - int write, unsigned long *paddr, int *pageshift) -{ - pgd_t *pgdp; - p4d_t *p4dp; - pud_t *pudp; - pmd_t *pmdp; - pte_t pte; - - pgdp = pgd_offset(vma->vm_mm, vaddr); - if (unlikely(pgd_none(*pgdp))) - goto err; - - p4dp = p4d_offset(pgdp, vaddr); - if (unlikely(p4d_none(*p4dp))) - goto err; - - pudp = pud_offset(p4dp, vaddr); - if (unlikely(pud_none(*pudp))) - goto err; - - pmdp = pmd_offset(pudp, vaddr); - if (unlikely(pmd_none(*pmdp))) - goto err; -#ifdef CONFIG_X86_64 - if (unlikely(pmd_leaf(*pmdp))) - pte = ptep_get((pte_t *)pmdp); - else -#endif - pte = *pte_offset_kernel(pmdp, vaddr); - - if (unlikely(!pte_present(pte) || - (write && (!pte_write(pte) || !pte_dirty(pte))))) - return 1; - - *paddr = pte_pfn(pte) << PAGE_SHIFT; -#ifdef CONFIG_HUGETLB_PAGE - *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT; -#else - *pageshift = PAGE_SHIFT; -#endif - return 0; - -err: - return 1; -} - -static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr, - int write, int atomic, unsigned long *gpa, int *pageshift) -{ - struct mm_struct *mm = gts->ts_mm; - struct vm_area_struct *vma; - unsigned long paddr; - int ret, ps; - - vma = find_vma(mm, vaddr); - if (!vma) - goto inval; - - /* - * Atomic lookup is faster & usually works even if called in non-atomic - * context. - */ - rmb(); /* Must/check ms_range_active before loading PTEs */ - ret = atomic_pte_lookup(vma, vaddr, write, &paddr, &ps); - if (ret) { - if (atomic) - goto upm; - if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps)) - goto inval; - } - if (is_gru_paddr(paddr)) - goto inval; - paddr = paddr & ~((1UL << ps) - 1); - *gpa = uv_soc_phys_ram_to_gpa(paddr); - *pageshift = ps; - return VTOP_SUCCESS; - -inval: - return VTOP_INVALID; -upm: - return VTOP_RETRY; -} - - -/* - * Flush a CBE from cache. The CBE is clean in the cache. Dirty the - * CBE cacheline so that the line will be written back to home agent. - * Otherwise the line may be silently dropped. This has no impact - * except on performance. - */ -static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe) -{ - if (unlikely(cbe)) { - cbe->cbrexecstatus = 0; /* make CL dirty */ - gru_flush_cache(cbe); - } -} - -/* - * Preload the TLB with entries that may be required. Currently, preloading - * is implemented only for BCOPY. Preload <tlb_preload_count> pages OR to - * the end of the bcopy tranfer, whichever is smaller. - */ -static void gru_preload_tlb(struct gru_state *gru, - struct gru_thread_state *gts, int atomic, - unsigned long fault_vaddr, int asid, int write, - unsigned char tlb_preload_count, - struct gru_tlb_fault_handle *tfh, - struct gru_control_block_extended *cbe) -{ - unsigned long vaddr = 0, gpa; - int ret, pageshift; - - if (cbe->opccpy != OP_BCOPY) - return; - - if (fault_vaddr == cbe->cbe_baddr0) - vaddr = fault_vaddr + GRU_CACHE_LINE_BYTES * cbe->cbe_src_cl - 1; - else if (fault_vaddr == cbe->cbe_baddr1) - vaddr = fault_vaddr + (1 << cbe->xtypecpy) * cbe->cbe_nelemcur - 1; - - fault_vaddr &= PAGE_MASK; - vaddr &= PAGE_MASK; - vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE); - - while (vaddr > fault_vaddr) { - ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); - if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write, - GRU_PAGESIZE(pageshift))) - return; - gru_dbg(grudev, - "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n", - atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, - vaddr, asid, write, pageshift, gpa); - vaddr -= PAGE_SIZE; - STAT(tlb_preload_page); - } -} - -/* - * Drop a TLB entry into the GRU. The fault is described by info in an TFH. - * Input: - * cb Address of user CBR. Null if not running in user context - * Return: - * 0 = dropin, exception, or switch to UPM successful - * 1 = range invalidate active - * < 0 = error code - * - */ -static int gru_try_dropin(struct gru_state *gru, - struct gru_thread_state *gts, - struct gru_tlb_fault_handle *tfh, - struct gru_instruction_bits *cbk) -{ - struct gru_control_block_extended *cbe = NULL; - unsigned char tlb_preload_count = gts->ts_tlb_preload_count; - int pageshift = 0, asid, write, ret, atomic = !cbk, indexway; - unsigned long gpa = 0, vaddr = 0; - - /* - * NOTE: The GRU contains magic hardware that eliminates races between - * TLB invalidates and TLB dropins. If an invalidate occurs - * in the window between reading the TFH and the subsequent TLB dropin, - * the dropin is ignored. This eliminates the need for additional locks. - */ - - /* - * Prefetch the CBE if doing TLB preloading - */ - if (unlikely(tlb_preload_count)) { - cbe = gru_tfh_to_cbe(tfh); - prefetchw(cbe); - } - - /* - * Error if TFH state is IDLE or FMM mode & the user issuing a UPM call. - * Might be a hardware race OR a stupid user. Ignore FMM because FMM - * is a transient state. - */ - if (tfh->status != TFHSTATUS_EXCEPTION) { - gru_flush_cache(tfh); - sync_core(); - if (tfh->status != TFHSTATUS_EXCEPTION) - goto failnoexception; - STAT(tfh_stale_on_fault); - } - if (tfh->state == TFHSTATE_IDLE) - goto failidle; - if (tfh->state == TFHSTATE_MISS_FMM && cbk) - goto failfmm; - - write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0; - vaddr = tfh->missvaddr; - asid = tfh->missasid; - indexway = tfh->indexway; - if (asid == 0) - goto failnoasid; - - rmb(); /* TFH must be cache resident before reading ms_range_active */ - - /* - * TFH is cache resident - at least briefly. Fail the dropin - * if a range invalidate is active. - */ - if (atomic_read(>s->ts_gms->ms_range_active)) - goto failactive; - - ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); - if (ret == VTOP_INVALID) - goto failinval; - if (ret == VTOP_RETRY) - goto failupm; - - if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) { - gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift); - if (atomic || !gru_update_cch(gts)) { - gts->ts_force_cch_reload = 1; - goto failupm; - } - } - - if (unlikely(cbe) && pageshift == PAGE_SHIFT) { - gru_preload_tlb(gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe); - gru_flush_cache_cbe(cbe); - } - - gru_cb_set_istatus_active(cbk); - gts->ustats.tlbdropin++; - tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, - GRU_PAGESIZE(pageshift)); - gru_dbg(grudev, - "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x," - " rw %d, ps %d, gpa 0x%lx\n", - atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, vaddr, asid, - indexway, write, pageshift, gpa); - STAT(tlb_dropin); - return 0; - -failnoasid: - /* No asid (delayed unload). */ - STAT(tlb_dropin_fail_no_asid); - gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - if (!cbk) - tfh_user_polling_mode(tfh); - else - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - return -EAGAIN; - -failupm: - /* Atomic failure switch CBR to UPM */ - tfh_user_polling_mode(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_upm); - gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - return 1; - -failfmm: - /* FMM state on UPM call */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_fmm); - gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state); - return 0; - -failnoexception: - /* TFH status did not show exception pending */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - if (cbk) - gru_flush_cache(cbk); - STAT(tlb_dropin_fail_no_exception); - gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n", - tfh, tfh->status, tfh->state); - return 0; - -failidle: - /* TFH state was idle - no miss pending */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - if (cbk) - gru_flush_cache(cbk); - STAT(tlb_dropin_fail_idle); - gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state); - return 0; - -failinval: - /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */ - tfh_exception(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_invalid); - gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - return -EFAULT; - -failactive: - /* Range invalidate active. Switch to UPM iff atomic */ - if (!cbk) - tfh_user_polling_mode(tfh); - else - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_range_active); - gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n", - tfh, vaddr); - return 1; -} - -/* - * Process an external interrupt from the GRU. This interrupt is - * caused by a TLB miss. - * Note that this is the interrupt handler that is registered with linux - * interrupt handlers. - */ -static irqreturn_t gru_intr(int chiplet, int blade) -{ - struct gru_state *gru; - struct gru_tlb_fault_map imap, dmap; - struct gru_thread_state *gts; - struct gru_tlb_fault_handle *tfh = NULL; - struct completion *cmp; - int cbrnum, ctxnum; - - STAT(intr); - - gru = &gru_base[blade]->bs_grus[chiplet]; - if (!gru) { - dev_err(grudev, "GRU: invalid interrupt: cpu %d, chiplet %d\n", - raw_smp_processor_id(), chiplet); - return IRQ_NONE; - } - get_clear_fault_map(gru, &imap, &dmap); - gru_dbg(grudev, - "cpu %d, chiplet %d, gid %d, imap %016lx %016lx, dmap %016lx %016lx\n", - smp_processor_id(), chiplet, gru->gs_gid, - imap.fault_bits[0], imap.fault_bits[1], - dmap.fault_bits[0], dmap.fault_bits[1]); - - for_each_cbr_in_tfm(cbrnum, dmap.fault_bits) { - STAT(intr_cbr); - cmp = gru->gs_blade->bs_async_wq; - if (cmp) - complete(cmp); - gru_dbg(grudev, "gid %d, cbr_done %d, done %d\n", - gru->gs_gid, cbrnum, cmp ? cmp->done : -1); - } - - for_each_cbr_in_tfm(cbrnum, imap.fault_bits) { - STAT(intr_tfh); - tfh = get_tfh_by_index(gru, cbrnum); - prefetchw(tfh); /* Helps on hdw, required for emulator */ - - /* - * When hardware sets a bit in the faultmap, it implicitly - * locks the GRU context so that it cannot be unloaded. - * The gts cannot change until a TFH start/writestart command - * is issued. - */ - ctxnum = tfh->ctxnum; - gts = gru->gs_gts[ctxnum]; - - /* Spurious interrupts can cause this. Ignore. */ - if (!gts) { - STAT(intr_spurious); - continue; - } - - /* - * This is running in interrupt context. Trylock the mmap_lock. - * If it fails, retry the fault in user context. - */ - gts->ustats.fmm_tlbmiss++; - if (!gts->ts_force_cch_reload && - mmap_read_trylock(gts->ts_mm)) { - gru_try_dropin(gru, gts, tfh, NULL); - mmap_read_unlock(gts->ts_mm); - } else { - tfh_user_polling_mode(tfh); - STAT(intr_mm_lock_failed); - } - } - return IRQ_HANDLED; -} - -irqreturn_t gru0_intr(int irq, void *dev_id) -{ - return gru_intr(0, uv_numa_blade_id()); -} - -irqreturn_t gru1_intr(int irq, void *dev_id) -{ - return gru_intr(1, uv_numa_blade_id()); -} - -irqreturn_t gru_intr_mblade(int irq, void *dev_id) -{ - int blade; - - for_each_possible_blade(blade) { - if (uv_blade_nr_possible_cpus(blade)) - continue; - gru_intr(0, blade); - gru_intr(1, blade); - } - return IRQ_HANDLED; -} - - -static int gru_user_dropin(struct gru_thread_state *gts, - struct gru_tlb_fault_handle *tfh, - void *cb) -{ - struct gru_mm_struct *gms = gts->ts_gms; - int ret; - - gts->ustats.upm_tlbmiss++; - while (1) { - wait_event(gms->ms_wait_queue, - atomic_read(&gms->ms_range_active) == 0); - prefetchw(tfh); /* Helps on hdw, required for emulator */ - ret = gru_try_dropin(gts->ts_gru, gts, tfh, cb); - if (ret <= 0) - return ret; - STAT(call_os_wait_queue); - } -} - -/* - * This interface is called as a result of a user detecting a "call OS" bit - * in a user CB. Normally means that a TLB fault has occurred. - * cb - user virtual address of the CB - */ -int gru_handle_user_call_os(unsigned long cb) -{ - struct gru_tlb_fault_handle *tfh; - struct gru_thread_state *gts; - void *cbk; - int ucbnum, cbrnum, ret = -EINVAL; - - STAT(call_os); - - /* sanity check the cb pointer */ - ucbnum = get_cb_number((void *)cb); - if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB) - return -EINVAL; - -again: - gts = gru_find_lock_gts(cb); - if (!gts) - return -EINVAL; - gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts); - - if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) - goto exit; - - if (gru_check_context_placement(gts)) { - gru_unlock_gts(gts); - gru_unload_context(gts, 1); - goto again; - } - - /* - * CCH may contain stale data if ts_force_cch_reload is set. - */ - if (gts->ts_gru && gts->ts_force_cch_reload) { - gts->ts_force_cch_reload = 0; - gru_update_cch(gts); - } - - ret = -EAGAIN; - cbrnum = thread_cbr_number(gts, ucbnum); - if (gts->ts_gru) { - tfh = get_tfh_by_index(gts->ts_gru, cbrnum); - cbk = get_gseg_base_address_cb(gts->ts_gru->gs_gru_base_vaddr, - gts->ts_ctxnum, ucbnum); - ret = gru_user_dropin(gts, tfh, cbk); - } -exit: - gru_unlock_gts(gts); - return ret; -} - -/* - * Fetch the exception detail information for a CB that terminated with - * an exception. - */ -int gru_get_exception_detail(unsigned long arg) -{ - struct control_block_extended_exc_detail excdet; - struct gru_control_block_extended *cbe; - struct gru_thread_state *gts; - int ucbnum, cbrnum, ret; - - STAT(user_exception); - if (copy_from_user(&excdet, (void __user *)arg, sizeof(excdet))) - return -EFAULT; - - gts = gru_find_lock_gts(excdet.cb); - if (!gts) - return -EINVAL; - - gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", excdet.cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts); - ucbnum = get_cb_number((void *)excdet.cb); - if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) { - ret = -EINVAL; - } else if (gts->ts_gru) { - cbrnum = thread_cbr_number(gts, ucbnum); - cbe = get_cbe_by_index(gts->ts_gru, cbrnum); - gru_flush_cache(cbe); /* CBE not coherent */ - sync_core(); /* make sure we are have current data */ - excdet.opc = cbe->opccpy; - excdet.exopc = cbe->exopccpy; - excdet.ecause = cbe->ecause; - excdet.exceptdet0 = cbe->idef1upd; - excdet.exceptdet1 = cbe->idef3upd; - excdet.cbrstate = cbe->cbrstate; - excdet.cbrexecstatus = cbe->cbrexecstatus; - gru_flush_cache_cbe(cbe); - ret = 0; - } else { - ret = -EAGAIN; - } - gru_unlock_gts(gts); - - gru_dbg(grudev, - "cb 0x%lx, op %d, exopc %d, cbrstate %d, cbrexecstatus 0x%x, ecause 0x%x, " - "exdet0 0x%lx, exdet1 0x%x\n", - excdet.cb, excdet.opc, excdet.exopc, excdet.cbrstate, excdet.cbrexecstatus, - excdet.ecause, excdet.exceptdet0, excdet.exceptdet1); - if (!ret && copy_to_user((void __user *)arg, &excdet, sizeof(excdet))) - ret = -EFAULT; - return ret; -} - -/* - * User request to unload a context. Content is saved for possible reload. - */ -static int gru_unload_all_contexts(void) -{ - struct gru_thread_state *gts; - struct gru_state *gru; - int gid, ctxnum; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - foreach_gid(gid) { - gru = GID_TO_GRU(gid); - spin_lock(&gru->gs_lock); - for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) { - gts = gru->gs_gts[ctxnum]; - if (gts && mutex_trylock(>s->ts_ctxlock)) { - spin_unlock(&gru->gs_lock); - gru_unload_context(gts, 1); - mutex_unlock(>s->ts_ctxlock); - spin_lock(&gru->gs_lock); - } - } - spin_unlock(&gru->gs_lock); - } - return 0; -} - -int gru_user_unload_context(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_unload_context_req req; - - STAT(user_unload_context); - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - gru_dbg(grudev, "gseg 0x%lx\n", req.gseg); - - if (!req.gseg) - return gru_unload_all_contexts(); - - gts = gru_find_lock_gts(req.gseg); - if (!gts) - return -EINVAL; - - if (gts->ts_gru) - gru_unload_context(gts, 1); - gru_unlock_gts(gts); - - return 0; -} - -/* - * User request to flush a range of virtual addresses from the GRU TLB - * (Mainly for testing). - */ -int gru_user_flush_tlb(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_flush_tlb_req req; - struct gru_mm_struct *gms; - - STAT(user_flush_tlb); - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - gru_dbg(grudev, "gseg 0x%lx, vaddr 0x%lx, len 0x%lx\n", req.gseg, - req.vaddr, req.len); - - gts = gru_find_lock_gts(req.gseg); - if (!gts) - return -EINVAL; - - gms = gts->ts_gms; - gru_unlock_gts(gts); - gru_flush_tlb_range(gms, req.vaddr, req.len); - - return 0; -} - -/* - * Fetch GSEG statisticss - */ -long gru_get_gseg_statistics(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_get_gseg_statistics_req req; - - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - /* - * The library creates arrays of contexts for threaded programs. - * If no gts exists in the array, the context has never been used & all - * statistics are implicitly 0. - */ - gts = gru_find_lock_gts(req.gseg); - if (gts) { - memcpy(&req.stats, >s->ustats, sizeof(gts->ustats)); - gru_unlock_gts(gts); - } else { - memset(&req.stats, 0, sizeof(gts->ustats)); - } - - if (copy_to_user((void __user *)arg, &req, sizeof(req))) - return -EFAULT; - - return 0; -} - -/* - * Register the current task as the user of the GSEG slice. - * Needed for TLB fault interrupt targeting. - */ -int gru_set_context_option(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_set_context_option_req req; - int ret = 0; - - STAT(set_context_option); - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1); - - gts = gru_find_lock_gts(req.gseg); - if (!gts) { - gts = gru_alloc_locked_gts(req.gseg); - if (IS_ERR(gts)) - return PTR_ERR(gts); - } - - switch (req.op) { - case sco_blade_chiplet: - /* Select blade/chiplet for GRU context */ - if (req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB || - req.val1 < -1 || req.val1 >= GRU_MAX_BLADES || - (req.val1 >= 0 && !gru_base[req.val1])) { - ret = -EINVAL; - } else { - gts->ts_user_blade_id = req.val1; - gts->ts_user_chiplet_id = req.val0; - if (gru_check_context_placement(gts)) { - gru_unlock_gts(gts); - gru_unload_context(gts, 1); - return ret; - } - } - break; - case sco_gseg_owner: - /* Register the current task as the GSEG owner */ - gts->ts_tgid_owner = current->tgid; - break; - case sco_cch_req_slice: - /* Set the CCH slice option */ - gts->ts_cch_req_slice = req.val1 & 3; - break; - default: - ret = -EINVAL; - } - gru_unlock_gts(gts); - - return ret; -} diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c deleted file mode 100644 index e755690c9805..000000000000 --- a/drivers/misc/sgi-gru/grufile.c +++ /dev/null @@ -1,540 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * FILE OPERATIONS & DRIVER INITIALIZATION - * - * This file supports the user system call for file open, close, mmap, etc. - * This also incudes the driver initialization code. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2008-2014 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/io.h> -#include <linux/spinlock.h> -#include <linux/device.h> -#include <linux/miscdevice.h> -#include <linux/interrupt.h> -#include <linux/proc_fs.h> -#include <linux/uaccess.h> -#ifdef CONFIG_X86_64 -#include <asm/uv/uv_irq.h> -#endif -#include <asm/uv/uv.h> -#include "gru.h" -#include "grulib.h" -#include "grutables.h" - -#include <asm/uv/uv_hub.h> -#include <asm/uv/uv_mmrs.h> - -struct gru_blade_state *gru_base[GRU_MAX_BLADES] __read_mostly; -unsigned long gru_start_paddr __read_mostly; -void *gru_start_vaddr __read_mostly; -unsigned long gru_end_paddr __read_mostly; -unsigned int gru_max_gids __read_mostly; -struct gru_stats_s gru_stats; - -/* Guaranteed user available resources on each node */ -static int max_user_cbrs, max_user_dsr_bytes; - -static struct miscdevice gru_miscdev; - -static int gru_supported(void) -{ - return is_uv_system() && - (uv_hub_info->hub_revision < UV3_HUB_REVISION_BASE); -} - -/* - * gru_vma_close - * - * Called when unmapping a device mapping. Frees all gru resources - * and tables belonging to the vma. - */ -static void gru_vma_close(struct vm_area_struct *vma) -{ - struct gru_vma_data *vdata; - struct gru_thread_state *gts; - struct list_head *entry, *next; - - if (!vma->vm_private_data) - return; - - vdata = vma->vm_private_data; - vma->vm_private_data = NULL; - gru_dbg(grudev, "vma %p, file %p, vdata %p\n", vma, vma->vm_file, - vdata); - list_for_each_safe(entry, next, &vdata->vd_head) { - gts = - list_entry(entry, struct gru_thread_state, ts_next); - list_del(>s->ts_next); - mutex_lock(>s->ts_ctxlock); - if (gts->ts_gru) - gru_unload_context(gts, 0); - mutex_unlock(>s->ts_ctxlock); - gts_drop(gts); - } - kfree(vdata); - STAT(vdata_free); -} - -/* - * gru_file_mmap - * - * Called when mmapping the device. Initializes the vma with a fault handler - * and private data structure necessary to allocate, track, and free the - * underlying pages. - */ -static int gru_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) != (VM_SHARED | VM_WRITE)) - return -EPERM; - - if (vma->vm_start & (GRU_GSEG_PAGESIZE - 1) || - vma->vm_end & (GRU_GSEG_PAGESIZE - 1)) - return -EINVAL; - - vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_LOCKED | - VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP); - vma->vm_page_prot = PAGE_SHARED; - vma->vm_ops = &gru_vm_ops; - - vma->vm_private_data = gru_alloc_vma_data(vma, 0); - if (!vma->vm_private_data) - return -ENOMEM; - - gru_dbg(grudev, "file %p, vaddr 0x%lx, vma %p, vdata %p\n", - file, vma->vm_start, vma, vma->vm_private_data); - return 0; -} - -/* - * Create a new GRU context - */ -static int gru_create_new_context(unsigned long arg) -{ - struct gru_create_context_req req; - struct vm_area_struct *vma; - struct gru_vma_data *vdata; - int ret = -EINVAL; - - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - if (req.data_segment_bytes > max_user_dsr_bytes) - return -EINVAL; - if (req.control_blocks > max_user_cbrs || !req.maximum_thread_count) - return -EINVAL; - - if (!(req.options & GRU_OPT_MISS_MASK)) - req.options |= GRU_OPT_MISS_FMM_INTR; - - mmap_write_lock(current->mm); - vma = gru_find_vma(req.gseg); - if (vma) { - vdata = vma->vm_private_data; - vdata->vd_user_options = req.options; - vdata->vd_dsr_au_count = - GRU_DS_BYTES_TO_AU(req.data_segment_bytes); - vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks); - vdata->vd_tlb_preload_count = req.tlb_preload_count; - ret = 0; - } - mmap_write_unlock(current->mm); - - return ret; -} - -/* - * Get GRU configuration info (temp - for emulator testing) - */ -static long gru_get_config_info(unsigned long arg) -{ - struct gru_config_info info; - int nodesperblade; - - if (num_online_nodes() > 1 && - (uv_node_to_blade_id(1) == uv_node_to_blade_id(0))) - nodesperblade = 2; - else - nodesperblade = 1; - memset(&info, 0, sizeof(info)); - info.cpus = num_online_cpus(); - info.nodes = num_online_nodes(); - info.blades = info.nodes / nodesperblade; - info.chiplets = GRU_CHIPLETS_PER_BLADE * info.blades; - - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; -} - -/* - * gru_file_unlocked_ioctl - * - * Called to update file attributes via IOCTL calls. - */ -static long gru_file_unlocked_ioctl(struct file *file, unsigned int req, - unsigned long arg) -{ - int err = -EBADRQC; - - gru_dbg(grudev, "file %p, req 0x%x, 0x%lx\n", file, req, arg); - - switch (req) { - case GRU_CREATE_CONTEXT: - err = gru_create_new_context(arg); - break; - case GRU_SET_CONTEXT_OPTION: - err = gru_set_context_option(arg); - break; - case GRU_USER_GET_EXCEPTION_DETAIL: - err = gru_get_exception_detail(arg); - break; - case GRU_USER_UNLOAD_CONTEXT: - err = gru_user_unload_context(arg); - break; - case GRU_USER_FLUSH_TLB: - err = gru_user_flush_tlb(arg); - break; - case GRU_USER_CALL_OS: - err = gru_handle_user_call_os(arg); - break; - case GRU_GET_GSEG_STATISTICS: - err = gru_get_gseg_statistics(arg); - break; - case GRU_KTEST: - err = gru_ktest(arg); - break; - case GRU_GET_CONFIG_INFO: - err = gru_get_config_info(arg); - break; - case GRU_DUMP_CHIPLET_STATE: - err = gru_dump_chiplet_request(arg); - break; - } - return err; -} - -/* - * Called at init time to build tables for all GRUs that are present in the - * system. - */ -static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr, - void *vaddr, int blade_id, int chiplet_id) -{ - spin_lock_init(&gru->gs_lock); - spin_lock_init(&gru->gs_asid_lock); - gru->gs_gru_base_paddr = paddr; - gru->gs_gru_base_vaddr = vaddr; - gru->gs_gid = blade_id * GRU_CHIPLETS_PER_BLADE + chiplet_id; - gru->gs_blade = gru_base[blade_id]; - gru->gs_blade_id = blade_id; - gru->gs_chiplet_id = chiplet_id; - gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1; - gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1; - gru->gs_asid_limit = MAX_ASID; - gru_tgh_flush_init(gru); - if (gru->gs_gid >= gru_max_gids) - gru_max_gids = gru->gs_gid + 1; - gru_dbg(grudev, "bid %d, gid %d, vaddr %p (0x%lx)\n", - blade_id, gru->gs_gid, gru->gs_gru_base_vaddr, - gru->gs_gru_base_paddr); -} - -static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr) -{ - int pnode, nid, bid, chip; - int cbrs, dsrbytes, n; - int order = get_order(sizeof(struct gru_blade_state)); - struct page *page; - struct gru_state *gru; - unsigned long paddr; - void *vaddr; - - max_user_cbrs = GRU_NUM_CB; - max_user_dsr_bytes = GRU_NUM_DSR_BYTES; - for_each_possible_blade(bid) { - pnode = uv_blade_to_pnode(bid); - nid = uv_blade_to_memory_nid(bid);/* -1 if no memory on blade */ - page = alloc_pages_node(nid, GFP_KERNEL, order); - if (!page) - goto fail; - gru_base[bid] = page_address(page); - memset(gru_base[bid], 0, sizeof(struct gru_blade_state)); - gru_base[bid]->bs_lru_gru = &gru_base[bid]->bs_grus[0]; - spin_lock_init(&gru_base[bid]->bs_lock); - init_rwsem(&gru_base[bid]->bs_kgts_sema); - - dsrbytes = 0; - cbrs = 0; - for (gru = gru_base[bid]->bs_grus, chip = 0; - chip < GRU_CHIPLETS_PER_BLADE; - chip++, gru++) { - paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip); - vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip); - gru_init_chiplet(gru, paddr, vaddr, bid, chip); - n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; - cbrs = max(cbrs, n); - n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; - dsrbytes = max(dsrbytes, n); - } - max_user_cbrs = min(max_user_cbrs, cbrs); - max_user_dsr_bytes = min(max_user_dsr_bytes, dsrbytes); - } - - return 0; - -fail: - for (bid--; bid >= 0; bid--) - free_pages((unsigned long)gru_base[bid], order); - return -ENOMEM; -} - -static void gru_free_tables(void) -{ - int bid; - int order = get_order(sizeof(struct gru_state) * - GRU_CHIPLETS_PER_BLADE); - - for (bid = 0; bid < GRU_MAX_BLADES; bid++) - free_pages((unsigned long)gru_base[bid], order); -} - -static unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep) -{ - unsigned long mmr = 0; - int core; - - /* - * We target the cores of a blade and not the hyperthreads themselves. - * There is a max of 8 cores per socket and 2 sockets per blade, - * making for a max total of 16 cores (i.e., 16 CPUs without - * hyperthreading and 32 CPUs with hyperthreading). - */ - core = uv_cpu_core_number(cpu) + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu); - if (core >= GRU_NUM_TFM || uv_cpu_ht_number(cpu)) - return 0; - - if (chiplet == 0) { - mmr = UVH_GR0_TLB_INT0_CONFIG + - core * (UVH_GR0_TLB_INT1_CONFIG - UVH_GR0_TLB_INT0_CONFIG); - } else if (chiplet == 1) { - mmr = UVH_GR1_TLB_INT0_CONFIG + - core * (UVH_GR1_TLB_INT1_CONFIG - UVH_GR1_TLB_INT0_CONFIG); - } else { - BUG(); - } - - *corep = core; - return mmr; -} - -static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name, - irq_handler_t irq_handler, int cpu, int blade) -{ - unsigned long mmr; - int irq, core; - int ret; - - mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); - if (mmr == 0) - return 0; - - irq = uv_setup_irq(irq_name, cpu, blade, mmr, UV_AFFINITY_CPU); - if (irq < 0) { - printk(KERN_ERR "%s: uv_setup_irq failed, errno=%d\n", - GRU_DRIVER_ID_STR, -irq); - return irq; - } - - ret = request_irq(irq, irq_handler, 0, irq_name, NULL); - if (ret) { - uv_teardown_irq(irq); - printk(KERN_ERR "%s: request_irq failed, errno=%d\n", - GRU_DRIVER_ID_STR, -ret); - return ret; - } - gru_base[blade]->bs_grus[chiplet].gs_irq[core] = irq; - return 0; -} - -static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade) -{ - int irq, core; - unsigned long mmr; - - mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); - if (mmr) { - irq = gru_base[blade]->bs_grus[chiplet].gs_irq[core]; - if (irq) { - free_irq(irq, NULL); - uv_teardown_irq(irq); - } - } -} - -static void gru_teardown_tlb_irqs(void) -{ - int blade; - int cpu; - - for_each_online_cpu(cpu) { - blade = uv_cpu_to_blade_id(cpu); - gru_chiplet_teardown_tlb_irq(0, cpu, blade); - gru_chiplet_teardown_tlb_irq(1, cpu, blade); - } - for_each_possible_blade(blade) { - if (uv_blade_nr_possible_cpus(blade)) - continue; - gru_chiplet_teardown_tlb_irq(0, 0, blade); - gru_chiplet_teardown_tlb_irq(1, 0, blade); - } -} - -static int gru_setup_tlb_irqs(void) -{ - int blade; - int cpu; - int ret; - - for_each_online_cpu(cpu) { - blade = uv_cpu_to_blade_id(cpu); - ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru0_intr, cpu, blade); - if (ret != 0) - goto exit1; - - ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru1_intr, cpu, blade); - if (ret != 0) - goto exit1; - } - for_each_possible_blade(blade) { - if (uv_blade_nr_possible_cpus(blade)) - continue; - ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru_intr_mblade, 0, blade); - if (ret != 0) - goto exit1; - - ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru_intr_mblade, 0, blade); - if (ret != 0) - goto exit1; - } - - return 0; - -exit1: - gru_teardown_tlb_irqs(); - return ret; -} - -/* - * gru_init - * - * Called at boot or module load time to initialize the GRUs. - */ -static int __init gru_init(void) -{ - int ret; - - if (!gru_supported()) - return 0; - - gru_start_paddr = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG) & - 0x7fffffffffffUL; - gru_start_vaddr = __va(gru_start_paddr); - gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE; - printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n", - gru_start_paddr, gru_end_paddr); - ret = misc_register(&gru_miscdev); - if (ret) { - printk(KERN_ERR "%s: misc_register failed\n", - GRU_DRIVER_ID_STR); - goto exit0; - } - - ret = gru_proc_init(); - if (ret) { - printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR); - goto exit1; - } - - ret = gru_init_tables(gru_start_paddr, gru_start_vaddr); - if (ret) { - printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR); - goto exit2; - } - - ret = gru_setup_tlb_irqs(); - if (ret != 0) - goto exit3; - - gru_kservices_init(); - - printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR, - GRU_DRIVER_VERSION_STR); - return 0; - -exit3: - gru_free_tables(); -exit2: - gru_proc_exit(); -exit1: - misc_deregister(&gru_miscdev); -exit0: - return ret; - -} - -static void __exit gru_exit(void) -{ - if (!gru_supported()) - return; - - gru_teardown_tlb_irqs(); - gru_kservices_exit(); - gru_free_tables(); - misc_deregister(&gru_miscdev); - gru_proc_exit(); - mmu_notifier_synchronize(); -} - -static const struct file_operations gru_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = gru_file_unlocked_ioctl, - .mmap = gru_file_mmap, - .llseek = noop_llseek, -}; - -static struct miscdevice gru_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "gru", - .fops = &gru_fops, -}; - -const struct vm_operations_struct gru_vm_ops = { - .close = gru_vma_close, - .fault = gru_fault, -}; - -#ifndef MODULE -fs_initcall(gru_init); -#else -module_init(gru_init); -#endif -module_exit(gru_exit); - -module_param(gru_options, ulong, 0644); -MODULE_PARM_DESC(gru_options, "Various debug options"); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION(GRU_DRIVER_ID_STR GRU_DRIVER_VERSION_STR); -MODULE_VERSION(GRU_DRIVER_VERSION_STR); - diff --git a/drivers/misc/sgi-gru/gruhandles.c b/drivers/misc/sgi-gru/gruhandles.c deleted file mode 100644 index 695316a83b01..000000000000 --- a/drivers/misc/sgi-gru/gruhandles.c +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * GRU KERNEL MCS INSTRUCTIONS - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include <linux/kernel.h> -#include "gru.h" -#include "grulib.h" -#include "grutables.h" - -/* 10 sec */ -#include <linux/sync_core.h> -#include <asm/tsc.h> -#define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) -#define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz) - -/* Extract the status field from a kernel handle */ -#define GET_MSEG_HANDLE_STATUS(h) (((*(unsigned long *)(h)) >> 16) & 3) - -struct mcs_op_statistic mcs_op_statistics[mcsop_last]; - -static void update_mcs_stats(enum mcs_op op, unsigned long clks) -{ - unsigned long nsec; - - nsec = CLKS2NSEC(clks); - atomic_long_inc(&mcs_op_statistics[op].count); - atomic_long_add(nsec, &mcs_op_statistics[op].total); - if (mcs_op_statistics[op].max < nsec) - mcs_op_statistics[op].max = nsec; -} - -static void start_instruction(void *h) -{ - unsigned long *w0 = h; - - wmb(); /* setting CMD/STATUS bits must be last */ - *w0 = *w0 | 0x20001; - gru_flush_cache(h); -} - -static void report_instruction_timeout(void *h) -{ - unsigned long goff = GSEGPOFF((unsigned long)h); - char *id = "???"; - - if (TYPE_IS(CCH, goff)) - id = "CCH"; - else if (TYPE_IS(TGH, goff)) - id = "TGH"; - else if (TYPE_IS(TFH, goff)) - id = "TFH"; - - panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id); -} - -static int wait_instruction_complete(void *h, enum mcs_op opc) -{ - int status; - unsigned long start_time = get_cycles(); - - while (1) { - cpu_relax(); - status = GET_MSEG_HANDLE_STATUS(h); - if (status != CCHSTATUS_ACTIVE) - break; - if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) { - report_instruction_timeout(h); - start_time = get_cycles(); - } - } - if (gru_options & OPT_STATS) - update_mcs_stats(opc, get_cycles() - start_time); - return status; -} - -int cch_allocate(struct gru_context_configuration_handle *cch) -{ - int ret; - - cch->opc = CCHOP_ALLOCATE; - start_instruction(cch); - ret = wait_instruction_complete(cch, cchop_allocate); - - /* - * Stop speculation into the GSEG being mapped by the previous ALLOCATE. - * The GSEG memory does not exist until the ALLOCATE completes. - */ - sync_core(); - return ret; -} - -int cch_start(struct gru_context_configuration_handle *cch) -{ - cch->opc = CCHOP_START; - start_instruction(cch); - return wait_instruction_complete(cch, cchop_start); -} - -int cch_interrupt(struct gru_context_configuration_handle *cch) -{ - cch->opc = CCHOP_INTERRUPT; - start_instruction(cch); - return wait_instruction_complete(cch, cchop_interrupt); -} - -int cch_deallocate(struct gru_context_configuration_handle *cch) -{ - int ret; - - cch->opc = CCHOP_DEALLOCATE; - start_instruction(cch); - ret = wait_instruction_complete(cch, cchop_deallocate); - - /* - * Stop speculation into the GSEG being unmapped by the previous - * DEALLOCATE. - */ - sync_core(); - return ret; -} - -int cch_interrupt_sync(struct gru_context_configuration_handle - *cch) -{ - cch->opc = CCHOP_INTERRUPT_SYNC; - start_instruction(cch); - return wait_instruction_complete(cch, cchop_interrupt_sync); -} - -int tgh_invalidate(struct gru_tlb_global_handle *tgh, - unsigned long vaddr, unsigned long vaddrmask, - int asid, int pagesize, int global, int n, - unsigned short ctxbitmap) -{ - tgh->vaddr = vaddr; - tgh->asid = asid; - tgh->pagesize = pagesize; - tgh->n = n; - tgh->global = global; - tgh->vaddrmask = vaddrmask; - tgh->ctxbitmap = ctxbitmap; - tgh->opc = TGHOP_TLBINV; - start_instruction(tgh); - return wait_instruction_complete(tgh, tghop_invalidate); -} - -int tfh_write_only(struct gru_tlb_fault_handle *tfh, - unsigned long paddr, int gaa, - unsigned long vaddr, int asid, int dirty, - int pagesize) -{ - tfh->fillasid = asid; - tfh->fillvaddr = vaddr; - tfh->pfn = paddr >> GRU_PADDR_SHIFT; - tfh->gaa = gaa; - tfh->dirty = dirty; - tfh->pagesize = pagesize; - tfh->opc = TFHOP_WRITE_ONLY; - start_instruction(tfh); - return wait_instruction_complete(tfh, tfhop_write_only); -} - -void tfh_write_restart(struct gru_tlb_fault_handle *tfh, - unsigned long paddr, int gaa, - unsigned long vaddr, int asid, int dirty, - int pagesize) -{ - tfh->fillasid = asid; - tfh->fillvaddr = vaddr; - tfh->pfn = paddr >> GRU_PADDR_SHIFT; - tfh->gaa = gaa; - tfh->dirty = dirty; - tfh->pagesize = pagesize; - tfh->opc = TFHOP_WRITE_RESTART; - start_instruction(tfh); -} - -void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh) -{ - tfh->opc = TFHOP_USER_POLLING_MODE; - start_instruction(tfh); -} - -void tfh_exception(struct gru_tlb_fault_handle *tfh) -{ - tfh->opc = TFHOP_EXCEPTION; - start_instruction(tfh); -} - diff --git a/drivers/misc/sgi-gru/gruhandles.h b/drivers/misc/sgi-gru/gruhandles.h deleted file mode 100644 index 5a498bf8d003..000000000000 --- a/drivers/misc/sgi-gru/gruhandles.h +++ /dev/null @@ -1,517 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SN Platform GRU Driver - * - * GRU HANDLE DEFINITION - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#ifndef __GRUHANDLES_H__ -#define __GRUHANDLES_H__ -#include "gru_instructions.h" - -/* - * Manifest constants for GRU Memory Map - */ -#define GRU_GSEG0_BASE 0 -#define GRU_MCS_BASE (64 * 1024 * 1024) -#define GRU_SIZE (128UL * 1024 * 1024) - -/* Handle & resource counts */ -#define GRU_NUM_CB 128 -#define GRU_NUM_DSR_BYTES (32 * 1024) -#define GRU_NUM_TFM 16 -#define GRU_NUM_TGH 24 -#define GRU_NUM_CBE 128 -#define GRU_NUM_TFH 128 -#define GRU_NUM_CCH 16 - -/* Maximum resource counts that can be reserved by user programs */ -#define GRU_NUM_USER_CBR GRU_NUM_CBE -#define GRU_NUM_USER_DSR_BYTES GRU_NUM_DSR_BYTES - -/* Bytes per handle & handle stride. Code assumes all cb, tfh, cbe handles - * are the same */ -#define GRU_HANDLE_BYTES 64 -#define GRU_HANDLE_STRIDE 256 - -/* Base addresses of handles */ -#define GRU_TFM_BASE (GRU_MCS_BASE + 0x00000) -#define GRU_TGH_BASE (GRU_MCS_BASE + 0x08000) -#define GRU_CBE_BASE (GRU_MCS_BASE + 0x10000) -#define GRU_TFH_BASE (GRU_MCS_BASE + 0x18000) -#define GRU_CCH_BASE (GRU_MCS_BASE + 0x20000) - -/* User gseg constants */ -#define GRU_GSEG_STRIDE (4 * 1024 * 1024) -#define GSEG_BASE(a) ((a) & ~(GRU_GSEG_PAGESIZE - 1)) - -/* Data segment constants */ -#define GRU_DSR_AU_BYTES 1024 -#define GRU_DSR_CL (GRU_NUM_DSR_BYTES / GRU_CACHE_LINE_BYTES) -#define GRU_DSR_AU_CL (GRU_DSR_AU_BYTES / GRU_CACHE_LINE_BYTES) -#define GRU_DSR_AU (GRU_NUM_DSR_BYTES / GRU_DSR_AU_BYTES) - -/* Control block constants */ -#define GRU_CBR_AU_SIZE 2 -#define GRU_CBR_AU (GRU_NUM_CBE / GRU_CBR_AU_SIZE) - -/* Convert resource counts to the number of AU */ -#define GRU_DS_BYTES_TO_AU(n) DIV_ROUND_UP(n, GRU_DSR_AU_BYTES) -#define GRU_CB_COUNT_TO_AU(n) DIV_ROUND_UP(n, GRU_CBR_AU_SIZE) - -/* UV limits */ -#define GRU_CHIPLETS_PER_HUB 2 -#define GRU_HUBS_PER_BLADE 1 -#define GRU_CHIPLETS_PER_BLADE (GRU_HUBS_PER_BLADE * GRU_CHIPLETS_PER_HUB) - -/* User GRU Gseg offsets */ -#define GRU_CB_BASE 0 -#define GRU_CB_LIMIT (GRU_CB_BASE + GRU_HANDLE_STRIDE * GRU_NUM_CBE) -#define GRU_DS_BASE 0x20000 -#define GRU_DS_LIMIT (GRU_DS_BASE + GRU_NUM_DSR_BYTES) - -/* Convert a GRU physical address to the chiplet offset */ -#define GSEGPOFF(h) ((h) & (GRU_SIZE - 1)) - -/* Convert an arbitrary handle address to the beginning of the GRU segment */ -#define GRUBASE(h) ((void *)((unsigned long)(h) & ~(GRU_SIZE - 1))) - -/* Test a valid handle address to determine the type */ -#define TYPE_IS(hn, h) ((h) >= GRU_##hn##_BASE && (h) < \ - GRU_##hn##_BASE + GRU_NUM_##hn * GRU_HANDLE_STRIDE && \ - (((h) & (GRU_HANDLE_STRIDE - 1)) == 0)) - - -/* General addressing macros. */ -static inline void *get_gseg_base_address(void *base, int ctxnum) -{ - return (void *)(base + GRU_GSEG0_BASE + GRU_GSEG_STRIDE * ctxnum); -} - -static inline void *get_gseg_base_address_cb(void *base, int ctxnum, int line) -{ - return (void *)(get_gseg_base_address(base, ctxnum) + - GRU_CB_BASE + GRU_HANDLE_STRIDE * line); -} - -static inline void *get_gseg_base_address_ds(void *base, int ctxnum, int line) -{ - return (void *)(get_gseg_base_address(base, ctxnum) + GRU_DS_BASE + - GRU_CACHE_LINE_BYTES * line); -} - -static inline struct gru_tlb_fault_map *get_tfm(void *base, int ctxnum) -{ - return (struct gru_tlb_fault_map *)(base + GRU_TFM_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_tlb_global_handle *get_tgh(void *base, int ctxnum) -{ - return (struct gru_tlb_global_handle *)(base + GRU_TGH_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_control_block_extended *get_cbe(void *base, int ctxnum) -{ - return (struct gru_control_block_extended *)(base + GRU_CBE_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_tlb_fault_handle *get_tfh(void *base, int ctxnum) -{ - return (struct gru_tlb_fault_handle *)(base + GRU_TFH_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_context_configuration_handle *get_cch(void *base, - int ctxnum) -{ - return (struct gru_context_configuration_handle *)(base + - GRU_CCH_BASE + ctxnum * GRU_HANDLE_STRIDE); -} - -static inline unsigned long get_cb_number(void *cb) -{ - return (((unsigned long)cb - GRU_CB_BASE) % GRU_GSEG_PAGESIZE) / - GRU_HANDLE_STRIDE; -} - -/* byte offset to a specific GRU chiplet. (p=pnode, c=chiplet (0 or 1)*/ -static inline unsigned long gru_chiplet_paddr(unsigned long paddr, int pnode, - int chiplet) -{ - return paddr + GRU_SIZE * (2 * pnode + chiplet); -} - -static inline void *gru_chiplet_vaddr(void *vaddr, int pnode, int chiplet) -{ - return vaddr + GRU_SIZE * (2 * pnode + chiplet); -} - -static inline struct gru_control_block_extended *gru_tfh_to_cbe( - struct gru_tlb_fault_handle *tfh) -{ - unsigned long cbe; - - cbe = (unsigned long)tfh - GRU_TFH_BASE + GRU_CBE_BASE; - return (struct gru_control_block_extended*)cbe; -} - - - - -/* - * Global TLB Fault Map - * Bitmap of outstanding TLB misses needing interrupt/polling service. - * - */ -struct gru_tlb_fault_map { - unsigned long fault_bits[BITS_TO_LONGS(GRU_NUM_CBE)]; - unsigned long fill0[2]; - unsigned long done_bits[BITS_TO_LONGS(GRU_NUM_CBE)]; - unsigned long fill1[2]; -}; - -/* - * TGH - TLB Global Handle - * Used for TLB flushing. - * - */ -struct gru_tlb_global_handle { - unsigned int cmd:1; /* DW 0 */ - unsigned int delresp:1; - unsigned int opc:1; - unsigned int fill1:5; - - unsigned int fill2:8; - - unsigned int status:2; - unsigned long fill3:2; - unsigned int state:3; - unsigned long fill4:1; - - unsigned int cause:3; - unsigned long fill5:37; - - unsigned long vaddr:64; /* DW 1 */ - - unsigned int asid:24; /* DW 2 */ - unsigned int fill6:8; - - unsigned int pagesize:5; - unsigned int fill7:11; - - unsigned int global:1; - unsigned int fill8:15; - - unsigned long vaddrmask:39; /* DW 3 */ - unsigned int fill9:9; - unsigned int n:10; - unsigned int fill10:6; - - unsigned int ctxbitmap:16; /* DW4 */ - unsigned long fill11[3]; -}; - -enum gru_tgh_cmd { - TGHCMD_START -}; - -enum gru_tgh_opc { - TGHOP_TLBNOP, - TGHOP_TLBINV -}; - -enum gru_tgh_status { - TGHSTATUS_IDLE, - TGHSTATUS_EXCEPTION, - TGHSTATUS_ACTIVE -}; - -enum gru_tgh_state { - TGHSTATE_IDLE, - TGHSTATE_PE_INVAL, - TGHSTATE_INTERRUPT_INVAL, - TGHSTATE_WAITDONE, - TGHSTATE_RESTART_CTX, -}; - -enum gru_tgh_cause { - TGHCAUSE_RR_ECC, - TGHCAUSE_TLB_ECC, - TGHCAUSE_LRU_ECC, - TGHCAUSE_PS_ECC, - TGHCAUSE_MUL_ERR, - TGHCAUSE_DATA_ERR, - TGHCAUSE_SW_FORCE -}; - - -/* - * TFH - TLB Global Handle - * Used for TLB dropins into the GRU TLB. - * - */ -struct gru_tlb_fault_handle { - unsigned int cmd:1; /* DW 0 - low 32*/ - unsigned int delresp:1; - unsigned int fill0:2; - unsigned int opc:3; - unsigned int fill1:9; - - unsigned int status:2; - unsigned int fill2:2; - unsigned int state:3; - unsigned int fill3:1; - - unsigned int cause:6; - unsigned int cb_int:1; - unsigned int fill4:1; - - unsigned int indexway:12; /* DW 0 - high 32 */ - unsigned int fill5:4; - - unsigned int ctxnum:4; - unsigned int fill6:12; - - unsigned long missvaddr:64; /* DW 1 */ - - unsigned int missasid:24; /* DW 2 */ - unsigned int fill7:8; - unsigned int fillasid:24; - unsigned int dirty:1; - unsigned int gaa:2; - unsigned long fill8:5; - - unsigned long pfn:41; /* DW 3 */ - unsigned int fill9:7; - unsigned int pagesize:5; - unsigned int fill10:11; - - unsigned long fillvaddr:64; /* DW 4 */ - - unsigned long fill11[3]; -}; - -enum gru_tfh_opc { - TFHOP_NOOP, - TFHOP_RESTART, - TFHOP_WRITE_ONLY, - TFHOP_WRITE_RESTART, - TFHOP_EXCEPTION, - TFHOP_USER_POLLING_MODE = 7, -}; - -enum tfh_status { - TFHSTATUS_IDLE, - TFHSTATUS_EXCEPTION, - TFHSTATUS_ACTIVE, -}; - -enum tfh_state { - TFHSTATE_INACTIVE, - TFHSTATE_IDLE, - TFHSTATE_MISS_UPM, - TFHSTATE_MISS_FMM, - TFHSTATE_HW_ERR, - TFHSTATE_WRITE_TLB, - TFHSTATE_RESTART_CBR, -}; - -/* TFH cause bits */ -enum tfh_cause { - TFHCAUSE_NONE, - TFHCAUSE_TLB_MISS, - TFHCAUSE_TLB_MOD, - TFHCAUSE_HW_ERROR_RR, - TFHCAUSE_HW_ERROR_MAIN_ARRAY, - TFHCAUSE_HW_ERROR_VALID, - TFHCAUSE_HW_ERROR_PAGESIZE, - TFHCAUSE_INSTRUCTION_EXCEPTION, - TFHCAUSE_UNCORRECTIBLE_ERROR, -}; - -/* GAA values */ -#define GAA_RAM 0x0 -#define GAA_NCRAM 0x2 -#define GAA_MMIO 0x1 -#define GAA_REGISTER 0x3 - -/* GRU paddr shift for pfn. (NOTE: shift is NOT by actual pagesize) */ -#define GRU_PADDR_SHIFT 12 - -/* - * Context Configuration handle - * Used to allocate resources to a GSEG context. - * - */ -struct gru_context_configuration_handle { - unsigned int cmd:1; /* DW0 */ - unsigned int delresp:1; - unsigned int opc:3; - unsigned int unmap_enable:1; - unsigned int req_slice_set_enable:1; - unsigned int req_slice:2; - unsigned int cb_int_enable:1; - unsigned int tlb_int_enable:1; - unsigned int tfm_fault_bit_enable:1; - unsigned int tlb_int_select:4; - - unsigned int status:2; - unsigned int state:2; - unsigned int reserved2:4; - - unsigned int cause:4; - unsigned int tfm_done_bit_enable:1; - unsigned int unused:3; - - unsigned int dsr_allocation_map; - - unsigned long cbr_allocation_map; /* DW1 */ - - unsigned int asid[8]; /* DW 2 - 5 */ - unsigned short sizeavail[8]; /* DW 6 - 7 */ -} __attribute__ ((packed)); - -enum gru_cch_opc { - CCHOP_START = 1, - CCHOP_ALLOCATE, - CCHOP_INTERRUPT, - CCHOP_DEALLOCATE, - CCHOP_INTERRUPT_SYNC, -}; - -enum gru_cch_status { - CCHSTATUS_IDLE, - CCHSTATUS_EXCEPTION, - CCHSTATUS_ACTIVE, -}; - -enum gru_cch_state { - CCHSTATE_INACTIVE, - CCHSTATE_MAPPED, - CCHSTATE_ACTIVE, - CCHSTATE_INTERRUPTED, -}; - -/* CCH Exception cause */ -enum gru_cch_cause { - CCHCAUSE_REGION_REGISTER_WRITE_ERROR = 1, - CCHCAUSE_ILLEGAL_OPCODE = 2, - CCHCAUSE_INVALID_START_REQUEST = 3, - CCHCAUSE_INVALID_ALLOCATION_REQUEST = 4, - CCHCAUSE_INVALID_DEALLOCATION_REQUEST = 5, - CCHCAUSE_INVALID_INTERRUPT_REQUEST = 6, - CCHCAUSE_CCH_BUSY = 7, - CCHCAUSE_NO_CBRS_TO_ALLOCATE = 8, - CCHCAUSE_BAD_TFM_CONFIG = 9, - CCHCAUSE_CBR_RESOURCES_OVERSUBSCRIPED = 10, - CCHCAUSE_DSR_RESOURCES_OVERSUBSCRIPED = 11, - CCHCAUSE_CBR_DEALLOCATION_ERROR = 12, -}; -/* - * CBE - Control Block Extended - * Maintains internal GRU state for active CBs. - * - */ -struct gru_control_block_extended { - unsigned int reserved0:1; /* DW 0 - low */ - unsigned int imacpy:3; - unsigned int reserved1:4; - unsigned int xtypecpy:3; - unsigned int iaa0cpy:2; - unsigned int iaa1cpy:2; - unsigned int reserved2:1; - unsigned int opccpy:8; - unsigned int exopccpy:8; - - unsigned int idef2cpy:22; /* DW 0 - high */ - unsigned int reserved3:10; - - unsigned int idef4cpy:22; /* DW 1 */ - unsigned int reserved4:10; - unsigned int idef4upd:22; - unsigned int reserved5:10; - - unsigned long idef1upd:64; /* DW 2 */ - - unsigned long idef5cpy:64; /* DW 3 */ - - unsigned long idef6cpy:64; /* DW 4 */ - - unsigned long idef3upd:64; /* DW 5 */ - - unsigned long idef5upd:64; /* DW 6 */ - - unsigned int idef2upd:22; /* DW 7 */ - unsigned int reserved6:10; - - unsigned int ecause:20; - unsigned int cbrstate:4; - unsigned int cbrexecstatus:8; -}; - -/* CBE fields for active BCOPY instructions */ -#define cbe_baddr0 idef1upd -#define cbe_baddr1 idef3upd -#define cbe_src_cl idef6cpy -#define cbe_nelemcur idef5upd - -enum gru_cbr_state { - CBRSTATE_INACTIVE, - CBRSTATE_IDLE, - CBRSTATE_PE_CHECK, - CBRSTATE_QUEUED, - CBRSTATE_WAIT_RESPONSE, - CBRSTATE_INTERRUPTED, - CBRSTATE_INTERRUPTED_MISS_FMM, - CBRSTATE_BUSY_INTERRUPT_MISS_FMM, - CBRSTATE_INTERRUPTED_MISS_UPM, - CBRSTATE_BUSY_INTERRUPTED_MISS_UPM, - CBRSTATE_REQUEST_ISSUE, - CBRSTATE_BUSY_INTERRUPT, -}; - -/* CBE cbrexecstatus bits - defined in gru_instructions.h*/ -/* CBE ecause bits - defined in gru_instructions.h */ - -/* - * Convert a processor pagesize into the strange encoded pagesize used by the - * GRU. Processor pagesize is encoded as log of bytes per page. (or PAGE_SHIFT) - * pagesize log pagesize grupagesize - * 4k 12 0 - * 16k 14 1 - * 64k 16 2 - * 256k 18 3 - * 1m 20 4 - * 2m 21 5 - * 4m 22 6 - * 16m 24 7 - * 64m 26 8 - * ... - */ -#define GRU_PAGESIZE(sh) ((((sh) > 20 ? (sh) + 2 : (sh)) >> 1) - 6) -#define GRU_SIZEAVAIL(sh) (1UL << GRU_PAGESIZE(sh)) - -/* minimum TLB purge count to ensure a full purge */ -#define GRUMAXINVAL 1024UL - -int cch_allocate(struct gru_context_configuration_handle *cch); -int cch_start(struct gru_context_configuration_handle *cch); -int cch_interrupt(struct gru_context_configuration_handle *cch); -int cch_deallocate(struct gru_context_configuration_handle *cch); -int cch_interrupt_sync(struct gru_context_configuration_handle *cch); -int tgh_invalidate(struct gru_tlb_global_handle *tgh, unsigned long vaddr, - unsigned long vaddrmask, int asid, int pagesize, int global, int n, - unsigned short ctxbitmap); -int tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long paddr, - int gaa, unsigned long vaddr, int asid, int dirty, int pagesize); -void tfh_write_restart(struct gru_tlb_fault_handle *tfh, unsigned long paddr, - int gaa, unsigned long vaddr, int asid, int dirty, int pagesize); -void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh); -void tfh_exception(struct gru_tlb_fault_handle *tfh); - -#endif /* __GRUHANDLES_H__ */ diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c deleted file mode 100644 index 9869f4f2f476..000000000000 --- a/drivers/misc/sgi-gru/grukdump.c +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * Dump GRU State - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/spinlock.h> -#include <linux/uaccess.h> -#include <linux/delay.h> -#include <linux/bitops.h> -#include <asm/uv/uv_hub.h> - -#include <linux/nospec.h> - -#include "gru.h" -#include "grutables.h" -#include "gruhandles.h" -#include "grulib.h" - -#define CCH_LOCK_ATTEMPTS 10 - -static int gru_user_copy_handle(void __user **dp, void *s) -{ - if (copy_to_user(*dp, s, GRU_HANDLE_BYTES)) - return -1; - *dp += GRU_HANDLE_BYTES; - return 0; -} - -static int gru_dump_context_data(void *grubase, - struct gru_context_configuration_handle *cch, - void __user *ubuf, int ctxnum, int dsrcnt, - int flush_cbrs) -{ - void *cb, *cbe, *tfh, *gseg; - int i, scr; - - gseg = grubase + ctxnum * GRU_GSEG_STRIDE; - cb = gseg + GRU_CB_BASE; - cbe = grubase + GRU_CBE_BASE; - tfh = grubase + GRU_TFH_BASE; - - for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) { - if (flush_cbrs) - gru_flush_cache(cb); - if (gru_user_copy_handle(&ubuf, cb)) - goto fail; - if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE)) - goto fail; - if (gru_user_copy_handle(&ubuf, cbe + i * GRU_HANDLE_STRIDE)) - goto fail; - cb += GRU_HANDLE_STRIDE; - } - if (dsrcnt) - memcpy(ubuf, gseg + GRU_DS_BASE, dsrcnt * GRU_HANDLE_STRIDE); - return 0; - -fail: - return -EFAULT; -} - -static int gru_dump_tfm(struct gru_state *gru, - void __user *ubuf, void __user *ubufend) -{ - struct gru_tlb_fault_map *tfm; - int i; - - if (GRU_NUM_TFM * GRU_CACHE_LINE_BYTES > ubufend - ubuf) - return -EFBIG; - - for (i = 0; i < GRU_NUM_TFM; i++) { - tfm = get_tfm(gru->gs_gru_base_vaddr, i); - if (gru_user_copy_handle(&ubuf, tfm)) - goto fail; - } - return GRU_NUM_TFM * GRU_CACHE_LINE_BYTES; - -fail: - return -EFAULT; -} - -static int gru_dump_tgh(struct gru_state *gru, - void __user *ubuf, void __user *ubufend) -{ - struct gru_tlb_global_handle *tgh; - int i; - - if (GRU_NUM_TGH * GRU_CACHE_LINE_BYTES > ubufend - ubuf) - return -EFBIG; - - for (i = 0; i < GRU_NUM_TGH; i++) { - tgh = get_tgh(gru->gs_gru_base_vaddr, i); - if (gru_user_copy_handle(&ubuf, tgh)) - goto fail; - } - return GRU_NUM_TGH * GRU_CACHE_LINE_BYTES; - -fail: - return -EFAULT; -} - -static int gru_dump_context(struct gru_state *gru, int ctxnum, - void __user *ubuf, void __user *ubufend, char data_opt, - char lock_cch, char flush_cbrs) -{ - struct gru_dump_context_header hdr; - struct gru_dump_context_header __user *uhdr = ubuf; - struct gru_context_configuration_handle *cch, *ubufcch; - struct gru_thread_state *gts; - int try, cch_locked, cbrcnt = 0, dsrcnt = 0, bytes = 0, ret = 0; - void *grubase; - - memset(&hdr, 0, sizeof(hdr)); - grubase = gru->gs_gru_base_vaddr; - cch = get_cch(grubase, ctxnum); - for (try = 0; try < CCH_LOCK_ATTEMPTS; try++) { - cch_locked = trylock_cch_handle(cch); - if (cch_locked) - break; - msleep(1); - } - - ubuf += sizeof(hdr); - ubufcch = ubuf; - if (gru_user_copy_handle(&ubuf, cch)) { - if (cch_locked) - unlock_cch_handle(cch); - return -EFAULT; - } - if (cch_locked) - ubufcch->delresp = 0; - bytes = sizeof(hdr) + GRU_CACHE_LINE_BYTES; - - if (cch_locked || !lock_cch) { - gts = gru->gs_gts[ctxnum]; - if (gts && gts->ts_vma) { - hdr.pid = gts->ts_tgid_owner; - hdr.vaddr = gts->ts_vma->vm_start; - } - if (cch->state != CCHSTATE_INACTIVE) { - cbrcnt = hweight64(cch->cbr_allocation_map) * - GRU_CBR_AU_SIZE; - dsrcnt = data_opt ? hweight32(cch->dsr_allocation_map) * - GRU_DSR_AU_CL : 0; - } - bytes += (3 * cbrcnt + dsrcnt) * GRU_CACHE_LINE_BYTES; - if (bytes > ubufend - ubuf) - ret = -EFBIG; - else - ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum, - dsrcnt, flush_cbrs); - } - if (cch_locked) - unlock_cch_handle(cch); - if (ret) - return ret; - - hdr.magic = GRU_DUMP_MAGIC; - hdr.gid = gru->gs_gid; - hdr.ctxnum = ctxnum; - hdr.cbrcnt = cbrcnt; - hdr.dsrcnt = dsrcnt; - hdr.cch_locked = cch_locked; - if (copy_to_user(uhdr, &hdr, sizeof(hdr))) - return -EFAULT; - - return bytes; -} - -int gru_dump_chiplet_request(unsigned long arg) -{ - struct gru_state *gru; - struct gru_dump_chiplet_state_req req; - void __user *ubuf; - void __user *ubufend; - int ctxnum, ret, cnt = 0; - - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - /* Currently, only dump by gid is implemented */ - if (req.gid >= gru_max_gids) - return -EINVAL; - req.gid = array_index_nospec(req.gid, gru_max_gids); - - gru = GID_TO_GRU(req.gid); - ubuf = req.buf; - ubufend = req.buf + req.buflen; - - ret = gru_dump_tfm(gru, ubuf, ubufend); - if (ret < 0) - goto fail; - ubuf += ret; - - ret = gru_dump_tgh(gru, ubuf, ubufend); - if (ret < 0) - goto fail; - ubuf += ret; - - for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) { - if (req.ctxnum == ctxnum || req.ctxnum < 0) { - ret = gru_dump_context(gru, ctxnum, ubuf, ubufend, - req.data_opt, req.lock_cch, - req.flush_cbrs); - if (ret < 0) - goto fail; - ubuf += ret; - cnt++; - } - } - - if (copy_to_user((void __user *)arg, &req, sizeof(req))) - return -EFAULT; - return cnt; - -fail: - return ret; -} diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c deleted file mode 100644 index 205945ce9e86..000000000000 --- a/drivers/misc/sgi-gru/grukservices.c +++ /dev/null @@ -1,1157 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * KERNEL SERVICES THAT USE THE GRU - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/spinlock.h> -#include <linux/device.h> -#include <linux/miscdevice.h> -#include <linux/proc_fs.h> -#include <linux/interrupt.h> -#include <linux/sync_core.h> -#include <linux/uaccess.h> -#include <linux/delay.h> -#include <linux/export.h> -#include <asm/io_apic.h> -#include "gru.h" -#include "grulib.h" -#include "grutables.h" -#include "grukservices.h" -#include "gru_instructions.h" -#include <asm/uv/uv_hub.h> - -/* - * Kernel GRU Usage - * - * The following is an interim algorithm for management of kernel GRU - * resources. This will likely be replaced when we better understand the - * kernel/user requirements. - * - * Blade percpu resources reserved for kernel use. These resources are - * reserved whenever the kernel context for the blade is loaded. Note - * that the kernel context is not guaranteed to be always available. It is - * loaded on demand & can be stolen by a user if the user demand exceeds the - * kernel demand. The kernel can always reload the kernel context but - * a SLEEP may be required!!!. - * - * Async Overview: - * - * Each blade has one "kernel context" that owns GRU kernel resources - * located on the blade. Kernel drivers use GRU resources in this context - * for sending messages, zeroing memory, etc. - * - * The kernel context is dynamically loaded on demand. If it is not in - * use by the kernel, the kernel context can be unloaded & given to a user. - * The kernel context will be reloaded when needed. This may require that - * a context be stolen from a user. - * NOTE: frequent unloading/reloading of the kernel context is - * expensive. We are depending on batch schedulers, cpusets, sane - * drivers or some other mechanism to prevent the need for frequent - * stealing/reloading. - * - * The kernel context consists of two parts: - * - 1 CB & a few DSRs that are reserved for each cpu on the blade. - * Each cpu has it's own private resources & does not share them - * with other cpus. These resources are used serially, ie, - * locked, used & unlocked on each call to a function in - * grukservices. - * (Now that we have dynamic loading of kernel contexts, I - * may rethink this & allow sharing between cpus....) - * - * - Additional resources can be reserved long term & used directly - * by UV drivers located in the kernel. Drivers using these GRU - * resources can use asynchronous GRU instructions that send - * interrupts on completion. - * - these resources must be explicitly locked/unlocked - * - locked resources prevent (obviously) the kernel - * context from being unloaded. - * - drivers using these resource directly issue their own - * GRU instruction and must wait/check completion. - * - * When these resources are reserved, the caller can optionally - * associate a wait_queue with the resources and use asynchronous - * GRU instructions. When an async GRU instruction completes, the - * driver will do a wakeup on the event. - * - */ - - -#define ASYNC_HAN_TO_BID(h) ((h) - 1) -#define ASYNC_BID_TO_HAN(b) ((b) + 1) -#define ASYNC_HAN_TO_BS(h) gru_base[ASYNC_HAN_TO_BID(h)] - -#define GRU_NUM_KERNEL_CBR 1 -#define GRU_NUM_KERNEL_DSR_BYTES 256 -#define GRU_NUM_KERNEL_DSR_CL (GRU_NUM_KERNEL_DSR_BYTES / \ - GRU_CACHE_LINE_BYTES) - -/* GRU instruction attributes for all instructions */ -#define IMA IMA_CB_DELAY - -/* GRU cacheline size is always 64 bytes - even on arches with 128 byte lines */ -#define __gru_cacheline_aligned__ \ - __attribute__((__aligned__(GRU_CACHE_LINE_BYTES))) - -#define MAGIC 0x1234567887654321UL - -/* Default retry count for GRU errors on kernel instructions */ -#define EXCEPTION_RETRY_LIMIT 3 - -/* Status of message queue sections */ -#define MQS_EMPTY 0 -#define MQS_FULL 1 -#define MQS_NOOP 2 - -/*----------------- RESOURCE MANAGEMENT -------------------------------------*/ -/* optimized for x86_64 */ -struct message_queue { - union gru_mesqhead head __gru_cacheline_aligned__; /* CL 0 */ - int qlines; /* DW 1 */ - long hstatus[2]; - void *next __gru_cacheline_aligned__;/* CL 1 */ - void *limit; - void *start; - void *start2; - char data ____cacheline_aligned; /* CL 2 */ -}; - -/* First word in every message - used by mesq interface */ -struct message_header { - char present; - char present2; - char lines; - char fill; -}; - -#define HSTATUS(mq, h) ((mq) + offsetof(struct message_queue, hstatus[h])) - -/* - * Reload the blade's kernel context into a GRU chiplet. Called holding - * the bs_kgts_sema for READ. Will steal user contexts if necessary. - */ -static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id) -{ - struct gru_state *gru; - struct gru_thread_state *kgts; - void *vaddr; - int ctxnum, ncpus; - - up_read(&bs->bs_kgts_sema); - down_write(&bs->bs_kgts_sema); - - if (!bs->bs_kgts) { - do { - bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0, 0); - if (!IS_ERR(bs->bs_kgts)) - break; - msleep(1); - } while (true); - bs->bs_kgts->ts_user_blade_id = blade_id; - } - kgts = bs->bs_kgts; - - if (!kgts->ts_gru) { - STAT(load_kernel_context); - ncpus = uv_blade_nr_possible_cpus(blade_id); - kgts->ts_cbr_au_count = GRU_CB_COUNT_TO_AU( - GRU_NUM_KERNEL_CBR * ncpus + bs->bs_async_cbrs); - kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU( - GRU_NUM_KERNEL_DSR_BYTES * ncpus + - bs->bs_async_dsr_bytes); - while (!gru_assign_gru_context(kgts)) { - msleep(1); - gru_steal_context(kgts); - } - gru_load_context(kgts); - gru = bs->bs_kgts->ts_gru; - vaddr = gru->gs_gru_base_vaddr; - ctxnum = kgts->ts_ctxnum; - bs->kernel_cb = get_gseg_base_address_cb(vaddr, ctxnum, 0); - bs->kernel_dsr = get_gseg_base_address_ds(vaddr, ctxnum, 0); - } - downgrade_write(&bs->bs_kgts_sema); -} - -/* - * Free all kernel contexts that are not currently in use. - * Returns 0 if all freed, else number of inuse context. - */ -static int gru_free_kernel_contexts(void) -{ - struct gru_blade_state *bs; - struct gru_thread_state *kgts; - int bid, ret = 0; - - for (bid = 0; bid < GRU_MAX_BLADES; bid++) { - bs = gru_base[bid]; - if (!bs) - continue; - - /* Ignore busy contexts. Don't want to block here. */ - if (down_write_trylock(&bs->bs_kgts_sema)) { - kgts = bs->bs_kgts; - if (kgts && kgts->ts_gru) - gru_unload_context(kgts, 0); - bs->bs_kgts = NULL; - up_write(&bs->bs_kgts_sema); - kfree(kgts); - } else { - ret++; - } - } - return ret; -} - -/* - * Lock & load the kernel context for the specified blade. - */ -static struct gru_blade_state *gru_lock_kernel_context(int blade_id) -{ - struct gru_blade_state *bs; - int bid; - - STAT(lock_kernel_context); -again: - bid = blade_id < 0 ? uv_numa_blade_id() : blade_id; - bs = gru_base[bid]; - - /* Handle the case where migration occurred while waiting for the sema */ - down_read(&bs->bs_kgts_sema); - if (blade_id < 0 && bid != uv_numa_blade_id()) { - up_read(&bs->bs_kgts_sema); - goto again; - } - if (!bs->bs_kgts || !bs->bs_kgts->ts_gru) - gru_load_kernel_context(bs, bid); - return bs; - -} - -/* - * Unlock the kernel context for the specified blade. Context is not - * unloaded but may be stolen before next use. - */ -static void gru_unlock_kernel_context(int blade_id) -{ - struct gru_blade_state *bs; - - bs = gru_base[blade_id]; - up_read(&bs->bs_kgts_sema); - STAT(unlock_kernel_context); -} - -/* - * Reserve & get pointers to the DSR/CBRs reserved for the current cpu. - * - returns with preemption disabled - */ -static int gru_get_cpu_resources(int dsr_bytes, void **cb, void **dsr) -{ - struct gru_blade_state *bs; - int lcpu; - - BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES); - bs = gru_lock_kernel_context(-1); - lcpu = uv_blade_processor_id(); - *cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE; - *dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES; - return 0; -} - -/* - * Free the current cpus reserved DSR/CBR resources. - */ -static void gru_free_cpu_resources(void *cb, void *dsr) -{ - gru_unlock_kernel_context(uv_numa_blade_id()); -} - -/* - * Reserve GRU resources to be used asynchronously. - * Note: currently supports only 1 reservation per blade. - * - * input: - * blade_id - blade on which resources should be reserved - * cbrs - number of CBRs - * dsr_bytes - number of DSR bytes needed - * output: - * handle to identify resource - * (0 = async resources already reserved) - */ -unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes, - struct completion *cmp) -{ - struct gru_blade_state *bs; - struct gru_thread_state *kgts; - int ret = 0; - - bs = gru_base[blade_id]; - - down_write(&bs->bs_kgts_sema); - - /* Verify no resources already reserved */ - if (bs->bs_async_dsr_bytes + bs->bs_async_cbrs) - goto done; - bs->bs_async_dsr_bytes = dsr_bytes; - bs->bs_async_cbrs = cbrs; - bs->bs_async_wq = cmp; - kgts = bs->bs_kgts; - - /* Resources changed. Unload context if already loaded */ - if (kgts && kgts->ts_gru) - gru_unload_context(kgts, 0); - ret = ASYNC_BID_TO_HAN(blade_id); - -done: - up_write(&bs->bs_kgts_sema); - return ret; -} - -/* - * Release async resources previously reserved. - * - * input: - * han - handle to identify resources - */ -void gru_release_async_resources(unsigned long han) -{ - struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han); - - down_write(&bs->bs_kgts_sema); - bs->bs_async_dsr_bytes = 0; - bs->bs_async_cbrs = 0; - bs->bs_async_wq = NULL; - up_write(&bs->bs_kgts_sema); -} - -/* - * Wait for async GRU instructions to complete. - * - * input: - * han - handle to identify resources - */ -void gru_wait_async_cbr(unsigned long han) -{ - struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han); - - wait_for_completion(bs->bs_async_wq); - mb(); -} - -/* - * Lock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - * output: - * cb - pointer to first CBR - * dsr - pointer to first DSR - */ -void gru_lock_async_resource(unsigned long han, void **cb, void **dsr) -{ - struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han); - int blade_id = ASYNC_HAN_TO_BID(han); - int ncpus; - - gru_lock_kernel_context(blade_id); - ncpus = uv_blade_nr_possible_cpus(blade_id); - if (cb) - *cb = bs->kernel_cb + ncpus * GRU_HANDLE_STRIDE; - if (dsr) - *dsr = bs->kernel_dsr + ncpus * GRU_NUM_KERNEL_DSR_BYTES; -} - -/* - * Unlock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - */ -void gru_unlock_async_resource(unsigned long han) -{ - int blade_id = ASYNC_HAN_TO_BID(han); - - gru_unlock_kernel_context(blade_id); -} - -/*----------------------------------------------------------------------*/ -int gru_get_cb_exception_detail(void *cb, - struct control_block_extended_exc_detail *excdet) -{ - struct gru_control_block_extended *cbe; - struct gru_thread_state *kgts = NULL; - unsigned long off; - int cbrnum, bid; - - /* - * Locate kgts for cb. This algorithm is SLOW but - * this function is rarely called (ie., almost never). - * Performance does not matter. - */ - for_each_possible_blade(bid) { - if (!gru_base[bid]) - break; - kgts = gru_base[bid]->bs_kgts; - if (!kgts || !kgts->ts_gru) - continue; - off = cb - kgts->ts_gru->gs_gru_base_vaddr; - if (off < GRU_SIZE) - break; - kgts = NULL; - } - BUG_ON(!kgts); - cbrnum = thread_cbr_number(kgts, get_cb_number(cb)); - cbe = get_cbe(GRUBASE(cb), cbrnum); - gru_flush_cache(cbe); /* CBE not coherent */ - sync_core(); - excdet->opc = cbe->opccpy; - excdet->exopc = cbe->exopccpy; - excdet->ecause = cbe->ecause; - excdet->exceptdet0 = cbe->idef1upd; - excdet->exceptdet1 = cbe->idef3upd; - gru_flush_cache(cbe); - return 0; -} - -static char *gru_get_cb_exception_detail_str(int ret, void *cb, - char *buf, int size) -{ - struct gru_control_block_status *gen = cb; - struct control_block_extended_exc_detail excdet; - - if (ret > 0 && gen->istatus == CBS_EXCEPTION) { - gru_get_cb_exception_detail(cb, &excdet); - snprintf(buf, size, - "GRU:%d exception: cb %p, opc %d, exopc %d, ecause 0x%x," - "excdet0 0x%lx, excdet1 0x%x", smp_processor_id(), - gen, excdet.opc, excdet.exopc, excdet.ecause, - excdet.exceptdet0, excdet.exceptdet1); - } else { - snprintf(buf, size, "No exception"); - } - return buf; -} - -static int gru_wait_idle_or_exception(struct gru_control_block_status *gen) -{ - while (gen->istatus >= CBS_ACTIVE) { - cpu_relax(); - barrier(); - } - return gen->istatus; -} - -static int gru_retry_exception(void *cb) -{ - struct gru_control_block_status *gen = cb; - struct control_block_extended_exc_detail excdet; - int retry = EXCEPTION_RETRY_LIMIT; - - while (1) { - if (gru_wait_idle_or_exception(gen) == CBS_IDLE) - return CBS_IDLE; - if (gru_get_cb_message_queue_substatus(cb)) - return CBS_EXCEPTION; - gru_get_cb_exception_detail(cb, &excdet); - if ((excdet.ecause & ~EXCEPTION_RETRY_BITS) || - (excdet.cbrexecstatus & CBR_EXS_ABORT_OCC)) - break; - if (retry-- == 0) - break; - gen->icmd = 1; - gru_flush_cache(gen); - } - return CBS_EXCEPTION; -} - -int gru_check_status_proc(void *cb) -{ - struct gru_control_block_status *gen = cb; - int ret; - - ret = gen->istatus; - if (ret == CBS_EXCEPTION) - ret = gru_retry_exception(cb); - rmb(); - return ret; - -} - -int gru_wait_proc(void *cb) -{ - struct gru_control_block_status *gen = cb; - int ret; - - ret = gru_wait_idle_or_exception(gen); - if (ret == CBS_EXCEPTION) - ret = gru_retry_exception(cb); - rmb(); - return ret; -} - -static void gru_abort(int ret, void *cb, char *str) -{ - char buf[GRU_EXC_STR_SIZE]; - - panic("GRU FATAL ERROR: %s - %s\n", str, - gru_get_cb_exception_detail_str(ret, cb, buf, sizeof(buf))); -} - -void gru_wait_abort_proc(void *cb) -{ - int ret; - - ret = gru_wait_proc(cb); - if (ret) - gru_abort(ret, cb, "gru_wait_abort"); -} - - -/*------------------------------ MESSAGE QUEUES -----------------------------*/ - -/* Internal status . These are NOT returned to the user. */ -#define MQIE_AGAIN -1 /* try again */ - - -/* - * Save/restore the "present" flag that is in the second line of 2-line - * messages - */ -static inline int get_present2(void *p) -{ - struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES; - return mhdr->present; -} - -static inline void restore_present2(void *p, int val) -{ - struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES; - mhdr->present = val; -} - -/* - * Create a message queue. - * qlines - message queue size in cache lines. Includes 2-line header. - */ -int gru_create_message_queue(struct gru_message_queue_desc *mqd, - void *p, unsigned int bytes, int nasid, int vector, int apicid) -{ - struct message_queue *mq = p; - unsigned int qlines; - - qlines = bytes / GRU_CACHE_LINE_BYTES - 2; - memset(mq, 0, bytes); - mq->start = &mq->data; - mq->start2 = &mq->data + (qlines / 2 - 1) * GRU_CACHE_LINE_BYTES; - mq->next = &mq->data; - mq->limit = &mq->data + (qlines - 2) * GRU_CACHE_LINE_BYTES; - mq->qlines = qlines; - mq->hstatus[0] = 0; - mq->hstatus[1] = 1; - mq->head = gru_mesq_head(2, qlines / 2 + 1); - mqd->mq = mq; - mqd->mq_gpa = uv_gpa(mq); - mqd->qlines = qlines; - mqd->interrupt_pnode = nasid >> 1; - mqd->interrupt_vector = vector; - mqd->interrupt_apicid = apicid; - return 0; -} -EXPORT_SYMBOL_GPL(gru_create_message_queue); - -/* - * Send a NOOP message to a message queue - * Returns: - * 0 - if queue is full after the send. This is the normal case - * but various races can change this. - * -1 - if mesq sent successfully but queue not full - * >0 - unexpected error. MQE_xxx returned - */ -static int send_noop_message(void *cb, struct gru_message_queue_desc *mqd, - void *mesg) -{ - const struct message_header noop_header = { - .present = MQS_NOOP, .lines = 1}; - unsigned long m; - int substatus, ret; - struct message_header save_mhdr, *mhdr = mesg; - - STAT(mesq_noop); - save_mhdr = *mhdr; - *mhdr = noop_header; - gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), 1, IMA); - ret = gru_wait(cb); - - if (ret) { - substatus = gru_get_cb_message_queue_substatus(cb); - switch (substatus) { - case CBSS_NO_ERROR: - STAT(mesq_noop_unexpected_error); - ret = MQE_UNEXPECTED_CB_ERR; - break; - case CBSS_LB_OVERFLOWED: - STAT(mesq_noop_lb_overflow); - ret = MQE_CONGESTION; - break; - case CBSS_QLIMIT_REACHED: - STAT(mesq_noop_qlimit_reached); - ret = 0; - break; - case CBSS_AMO_NACKED: - STAT(mesq_noop_amo_nacked); - ret = MQE_CONGESTION; - break; - case CBSS_PUT_NACKED: - STAT(mesq_noop_put_nacked); - m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6); - gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, 1, 1, - IMA); - if (gru_wait(cb) == CBS_IDLE) - ret = MQIE_AGAIN; - else - ret = MQE_UNEXPECTED_CB_ERR; - break; - case CBSS_PAGE_OVERFLOW: - STAT(mesq_noop_page_overflow); - fallthrough; - default: - BUG(); - } - } - *mhdr = save_mhdr; - return ret; -} - -/* - * Handle a gru_mesq full. - */ -static int send_message_queue_full(void *cb, struct gru_message_queue_desc *mqd, - void *mesg, int lines) -{ - union gru_mesqhead mqh; - unsigned int limit, head; - unsigned long avalue; - int half, qlines; - - /* Determine if switching to first/second half of q */ - avalue = gru_get_amo_value(cb); - head = gru_get_amo_value_head(cb); - limit = gru_get_amo_value_limit(cb); - - qlines = mqd->qlines; - half = (limit != qlines); - - if (half) - mqh = gru_mesq_head(qlines / 2 + 1, qlines); - else - mqh = gru_mesq_head(2, qlines / 2 + 1); - - /* Try to get lock for switching head pointer */ - gru_gamir(cb, EOP_IR_CLR, HSTATUS(mqd->mq_gpa, half), XTYPE_DW, IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - if (!gru_get_amo_value(cb)) { - STAT(mesq_qf_locked); - return MQE_QUEUE_FULL; - } - - /* Got the lock. Send optional NOP if queue not full, */ - if (head != limit) { - if (send_noop_message(cb, mqd, mesg)) { - gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half), - XTYPE_DW, IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - STAT(mesq_qf_noop_not_full); - return MQIE_AGAIN; - } - avalue++; - } - - /* Then flip queuehead to other half of queue. */ - gru_gamer(cb, EOP_ERR_CSWAP, mqd->mq_gpa, XTYPE_DW, mqh.val, avalue, - IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - - /* If not successfully in swapping queue head, clear the hstatus lock */ - if (gru_get_amo_value(cb) != avalue) { - STAT(mesq_qf_switch_head_failed); - gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half), XTYPE_DW, - IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - } - return MQIE_AGAIN; -cberr: - STAT(mesq_qf_unexpected_error); - return MQE_UNEXPECTED_CB_ERR; -} - -/* - * Handle a PUT failure. Note: if message was a 2-line message, one of the - * lines might have successfully have been written. Before sending the - * message, "present" must be cleared in BOTH lines to prevent the receiver - * from prematurely seeing the full message. - */ -static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd, - void *mesg, int lines) -{ - unsigned long m; - int ret, loops = 200; /* experimentally determined */ - - m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6); - if (lines == 2) { - gru_vset(cb, m, 0, XTYPE_CL, lines, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) - return MQE_UNEXPECTED_CB_ERR; - } - gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) - return MQE_UNEXPECTED_CB_ERR; - - if (!mqd->interrupt_vector) - return MQE_OK; - - /* - * Send a noop message in order to deliver a cross-partition interrupt - * to the SSI that contains the target message queue. Normally, the - * interrupt is automatically delivered by hardware following mesq - * operations, but some error conditions require explicit delivery. - * The noop message will trigger delivery. Otherwise partition failures - * could cause unrecovered errors. - */ - do { - ret = send_noop_message(cb, mqd, mesg); - } while ((ret == MQIE_AGAIN || ret == MQE_CONGESTION) && (loops-- > 0)); - - if (ret == MQIE_AGAIN || ret == MQE_CONGESTION) { - /* - * Don't indicate to the app to resend the message, as it's - * already been successfully sent. We simply send an OK - * (rather than fail the send with MQE_UNEXPECTED_CB_ERR), - * assuming that the other side is receiving enough - * interrupts to get this message processed anyway. - */ - ret = MQE_OK; - } - return ret; -} - -/* - * Handle a gru_mesq failure. Some of these failures are software recoverable - * or retryable. - */ -static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd, - void *mesg, int lines) -{ - int substatus, ret = 0; - - substatus = gru_get_cb_message_queue_substatus(cb); - switch (substatus) { - case CBSS_NO_ERROR: - STAT(mesq_send_unexpected_error); - ret = MQE_UNEXPECTED_CB_ERR; - break; - case CBSS_LB_OVERFLOWED: - STAT(mesq_send_lb_overflow); - ret = MQE_CONGESTION; - break; - case CBSS_QLIMIT_REACHED: - STAT(mesq_send_qlimit_reached); - ret = send_message_queue_full(cb, mqd, mesg, lines); - break; - case CBSS_AMO_NACKED: - STAT(mesq_send_amo_nacked); - ret = MQE_CONGESTION; - break; - case CBSS_PUT_NACKED: - STAT(mesq_send_put_nacked); - ret = send_message_put_nacked(cb, mqd, mesg, lines); - break; - case CBSS_PAGE_OVERFLOW: - STAT(mesq_page_overflow); - fallthrough; - default: - BUG(); - } - return ret; -} - -/* - * Send a message to a message queue - * mqd message queue descriptor - * mesg message. ust be vaddr within a GSEG - * bytes message size (<= 2 CL) - */ -int gru_send_message_gpa(struct gru_message_queue_desc *mqd, void *mesg, - unsigned int bytes) -{ - struct message_header *mhdr; - void *cb; - void *dsr; - int istatus, clines, ret; - - STAT(mesq_send); - BUG_ON(bytes < sizeof(int) || bytes > 2 * GRU_CACHE_LINE_BYTES); - - clines = DIV_ROUND_UP(bytes, GRU_CACHE_LINE_BYTES); - if (gru_get_cpu_resources(bytes, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - memcpy(dsr, mesg, bytes); - mhdr = dsr; - mhdr->present = MQS_FULL; - mhdr->lines = clines; - if (clines == 2) { - mhdr->present2 = get_present2(mhdr); - restore_present2(mhdr, MQS_FULL); - } - - do { - ret = MQE_OK; - gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), clines, IMA); - istatus = gru_wait(cb); - if (istatus != CBS_IDLE) - ret = send_message_failure(cb, mqd, dsr, clines); - } while (ret == MQIE_AGAIN); - gru_free_cpu_resources(cb, dsr); - - if (ret) - STAT(mesq_send_failed); - return ret; -} -EXPORT_SYMBOL_GPL(gru_send_message_gpa); - -/* - * Advance the receive pointer for the queue to the next message. - */ -void gru_free_message(struct gru_message_queue_desc *mqd, void *mesg) -{ - struct message_queue *mq = mqd->mq; - struct message_header *mhdr = mq->next; - void *next, *pnext; - int half = -1; - int lines = mhdr->lines; - - if (lines == 2) - restore_present2(mhdr, MQS_EMPTY); - mhdr->present = MQS_EMPTY; - - pnext = mq->next; - next = pnext + GRU_CACHE_LINE_BYTES * lines; - if (next == mq->limit) { - next = mq->start; - half = 1; - } else if (pnext < mq->start2 && next >= mq->start2) { - half = 0; - } - - if (half >= 0) - mq->hstatus[half] = 1; - mq->next = next; -} -EXPORT_SYMBOL_GPL(gru_free_message); - -/* - * Get next message from message queue. Return NULL if no message - * present. User must call next_message() to move to next message. - * rmq message queue - */ -void *gru_get_next_message(struct gru_message_queue_desc *mqd) -{ - struct message_queue *mq = mqd->mq; - struct message_header *mhdr = mq->next; - int present = mhdr->present; - - /* skip NOOP messages */ - while (present == MQS_NOOP) { - gru_free_message(mqd, mhdr); - mhdr = mq->next; - present = mhdr->present; - } - - /* Wait for both halves of 2 line messages */ - if (present == MQS_FULL && mhdr->lines == 2 && - get_present2(mhdr) == MQS_EMPTY) - present = MQS_EMPTY; - - if (!present) { - STAT(mesq_receive_none); - return NULL; - } - - if (mhdr->lines == 2) - restore_present2(mhdr, mhdr->present2); - - STAT(mesq_receive); - return mhdr; -} -EXPORT_SYMBOL_GPL(gru_get_next_message); - -/* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/ - -/* - * Load a DW from a global GPA. The GPA can be a memory or MMR address. - */ -int gru_read_gpa(unsigned long *value, unsigned long gpa) -{ - void *cb; - void *dsr; - int ret, iaa; - - STAT(read_gpa); - if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - iaa = gpa >> 62; - gru_vload_phys(cb, gpa, gru_get_tri(dsr), iaa, IMA); - ret = gru_wait(cb); - if (ret == CBS_IDLE) - *value = *(unsigned long *)dsr; - gru_free_cpu_resources(cb, dsr); - return ret; -} -EXPORT_SYMBOL_GPL(gru_read_gpa); - - -/* - * Copy a block of data using the GRU resources - */ -int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa, - unsigned int bytes) -{ - void *cb; - void *dsr; - int ret; - - STAT(copy_gpa); - if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - gru_bcopy(cb, src_gpa, dest_gpa, gru_get_tri(dsr), - XTYPE_B, bytes, GRU_NUM_KERNEL_DSR_CL, IMA); - ret = gru_wait(cb); - gru_free_cpu_resources(cb, dsr); - return ret; -} -EXPORT_SYMBOL_GPL(gru_copy_gpa); - -/* ------------------- KERNEL QUICKTESTS RUN AT STARTUP ----------------*/ -/* Temp - will delete after we gain confidence in the GRU */ - -static int quicktest0(unsigned long arg) -{ - unsigned long word0; - unsigned long word1; - void *cb; - void *dsr; - unsigned long *p; - int ret = -EIO; - - if (gru_get_cpu_resources(GRU_CACHE_LINE_BYTES, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - p = dsr; - word0 = MAGIC; - word1 = 0; - - gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) { - printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 1\n", smp_processor_id()); - goto done; - } - - if (*p != MAGIC) { - printk(KERN_DEBUG "GRU:%d quicktest0 bad magic 0x%lx\n", smp_processor_id(), *p); - goto done; - } - gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) { - printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 2\n", smp_processor_id()); - goto done; - } - - if (word0 != word1 || word1 != MAGIC) { - printk(KERN_DEBUG - "GRU:%d quicktest0 err: found 0x%lx, expected 0x%lx\n", - smp_processor_id(), word1, MAGIC); - goto done; - } - ret = 0; - -done: - gru_free_cpu_resources(cb, dsr); - return ret; -} - -#define ALIGNUP(p, q) ((void *)(((unsigned long)(p) + (q) - 1) & ~(q - 1))) - -static int quicktest1(unsigned long arg) -{ - struct gru_message_queue_desc mqd; - void *p, *mq; - int i, ret = -EIO; - char mes[GRU_CACHE_LINE_BYTES], *m; - - /* Need 1K cacheline aligned that does not cross page boundary */ - p = kmalloc(4096, 0); - if (p == NULL) - return -ENOMEM; - mq = ALIGNUP(p, 1024); - memset(mes, 0xee, sizeof(mes)); - - gru_create_message_queue(&mqd, mq, 8 * GRU_CACHE_LINE_BYTES, 0, 0, 0); - for (i = 0; i < 6; i++) { - mes[8] = i; - do { - ret = gru_send_message_gpa(&mqd, mes, sizeof(mes)); - } while (ret == MQE_CONGESTION); - if (ret) - break; - } - if (ret != MQE_QUEUE_FULL || i != 4) { - printk(KERN_DEBUG "GRU:%d quicktest1: unexpected status %d, i %d\n", - smp_processor_id(), ret, i); - goto done; - } - - for (i = 0; i < 6; i++) { - m = gru_get_next_message(&mqd); - if (!m || m[8] != i) - break; - gru_free_message(&mqd, m); - } - if (i != 4) { - printk(KERN_DEBUG "GRU:%d quicktest2: bad message, i %d, m %p, m8 %d\n", - smp_processor_id(), i, m, m ? m[8] : -1); - goto done; - } - ret = 0; - -done: - kfree(p); - return ret; -} - -static int quicktest2(unsigned long arg) -{ - static DECLARE_COMPLETION(cmp); - unsigned long han; - int blade_id = 0; - int numcb = 4; - int ret = 0; - unsigned long *buf; - void *cb0, *cb; - struct gru_control_block_status *gen; - int i, k, istatus, bytes; - - bytes = numcb * 4 * 8; - buf = kmalloc(bytes, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = -EBUSY; - han = gru_reserve_async_resources(blade_id, numcb, 0, &cmp); - if (!han) - goto done; - - gru_lock_async_resource(han, &cb0, NULL); - memset(buf, 0xee, bytes); - for (i = 0; i < numcb; i++) - gru_vset(cb0 + i * GRU_HANDLE_STRIDE, uv_gpa(&buf[i * 4]), 0, - XTYPE_DW, 4, 1, IMA_INTERRUPT); - - ret = 0; - k = numcb; - do { - gru_wait_async_cbr(han); - for (i = 0; i < numcb; i++) { - cb = cb0 + i * GRU_HANDLE_STRIDE; - istatus = gru_check_status(cb); - if (istatus != CBS_ACTIVE && istatus != CBS_CALL_OS) - break; - } - if (i == numcb) - continue; - if (istatus != CBS_IDLE) { - printk(KERN_DEBUG "GRU:%d quicktest2: cb %d, exception\n", smp_processor_id(), i); - ret = -EFAULT; - } else if (buf[4 * i] || buf[4 * i + 1] || buf[4 * i + 2] || - buf[4 * i + 3]) { - printk(KERN_DEBUG "GRU:%d quicktest2:cb %d, buf 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", - smp_processor_id(), i, buf[4 * i], buf[4 * i + 1], buf[4 * i + 2], buf[4 * i + 3]); - ret = -EIO; - } - k--; - gen = cb; - gen->istatus = CBS_CALL_OS; /* don't handle this CBR again */ - } while (k); - BUG_ON(cmp.done); - - gru_unlock_async_resource(han); - gru_release_async_resources(han); -done: - kfree(buf); - return ret; -} - -#define BUFSIZE 200 -static int quicktest3(unsigned long arg) -{ - char buf1[BUFSIZE], buf2[BUFSIZE]; - int ret = 0; - - memset(buf2, 0, sizeof(buf2)); - memset(buf1, get_cycles() & 255, sizeof(buf1)); - gru_copy_gpa(uv_gpa(buf2), uv_gpa(buf1), BUFSIZE); - if (memcmp(buf1, buf2, BUFSIZE)) { - printk(KERN_DEBUG "GRU:%d quicktest3 error\n", smp_processor_id()); - ret = -EIO; - } - return ret; -} - -/* - * Debugging only. User hook for various kernel tests - * of driver & gru. - */ -int gru_ktest(unsigned long arg) -{ - int ret = -EINVAL; - - switch (arg & 0xff) { - case 0: - ret = quicktest0(arg); - break; - case 1: - ret = quicktest1(arg); - break; - case 2: - ret = quicktest2(arg); - break; - case 3: - ret = quicktest3(arg); - break; - case 99: - ret = gru_free_kernel_contexts(); - break; - } - return ret; - -} - -int gru_kservices_init(void) -{ - return 0; -} - -void gru_kservices_exit(void) -{ - if (gru_free_kernel_contexts()) - BUG(); -} - diff --git a/drivers/misc/sgi-gru/grukservices.h b/drivers/misc/sgi-gru/grukservices.h deleted file mode 100644 index 510e45e9737e..000000000000 --- a/drivers/misc/sgi-gru/grukservices.h +++ /dev/null @@ -1,201 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ -#ifndef __GRU_KSERVICES_H_ -#define __GRU_KSERVICES_H_ - - -/* - * Message queues using the GRU to send/receive messages. - * - * These function allow the user to create a message queue for - * sending/receiving 1 or 2 cacheline messages using the GRU. - * - * Processes SENDING messages will use a kernel CBR/DSR to send - * the message. This is transparent to the caller. - * - * The receiver does not use any GRU resources. - * - * The functions support: - * - single receiver - * - multiple senders - * - cross partition message - * - * Missing features ZZZ: - * - user options for dealing with timeouts, queue full, etc. - * - gru_create_message_queue() needs interrupt vector info - */ - -struct gru_message_queue_desc { - void *mq; /* message queue vaddress */ - unsigned long mq_gpa; /* global address of mq */ - int qlines; /* queue size in CL */ - int interrupt_vector; /* interrupt vector */ - int interrupt_pnode; /* pnode for interrupt */ - int interrupt_apicid; /* lapicid for interrupt */ -}; - -/* - * Initialize a user allocated chunk of memory to be used as - * a message queue. The caller must ensure that the queue is - * in contiguous physical memory and is cacheline aligned. - * - * Message queue size is the total number of bytes allocated - * to the queue including a 2 cacheline header that is used - * to manage the queue. - * - * Input: - * mqd pointer to message queue descriptor - * p pointer to user allocated mesq memory. - * bytes size of message queue in bytes - * vector interrupt vector (zero if no interrupts) - * nasid nasid of blade where interrupt is delivered - * apicid apicid of cpu for interrupt - * - * Errors: - * 0 OK - * >0 error - */ -extern int gru_create_message_queue(struct gru_message_queue_desc *mqd, - void *p, unsigned int bytes, int nasid, int vector, int apicid); - -/* - * Send a message to a message queue. - * - * Note: The message queue transport mechanism uses the first 32 - * bits of the message. Users should avoid using these bits. - * - * - * Input: - * mqd pointer to message queue descriptor - * mesg pointer to message. Must be 64-bit aligned - * bytes size of message in bytes - * - * Output: - * 0 message sent - * >0 Send failure - see error codes below - * - */ -extern int gru_send_message_gpa(struct gru_message_queue_desc *mqd, - void *mesg, unsigned int bytes); - -/* Status values for gru_send_message() */ -#define MQE_OK 0 /* message sent successfully */ -#define MQE_CONGESTION 1 /* temporary congestion, try again */ -#define MQE_QUEUE_FULL 2 /* queue is full */ -#define MQE_UNEXPECTED_CB_ERR 3 /* unexpected CB error */ -#define MQE_PAGE_OVERFLOW 10 /* BUG - queue overflowed a page */ -#define MQE_BUG_NO_RESOURCES 11 /* BUG - could not alloc GRU cb/dsr */ - -/* - * Advance the receive pointer for the message queue to the next message. - * Note: current API requires messages to be gotten & freed in order. Future - * API extensions may allow for out-of-order freeing. - * - * Input - * mqd pointer to message queue descriptor - * mesq message being freed - */ -extern void gru_free_message(struct gru_message_queue_desc *mqd, - void *mesq); - -/* - * Get next message from message queue. Returns pointer to - * message OR NULL if no message present. - * User must call gru_free_message() after message is processed - * in order to move the queue pointers to next message. - * - * Input - * mqd pointer to message queue descriptor - * - * Output: - * p pointer to message - * NULL no message available - */ -extern void *gru_get_next_message(struct gru_message_queue_desc *mqd); - - -/* - * Read a GRU global GPA. Source can be located in a remote partition. - * - * Input: - * value memory address where MMR value is returned - * gpa source numalink physical address of GPA - * - * Output: - * 0 OK - * >0 error - */ -int gru_read_gpa(unsigned long *value, unsigned long gpa); - - -/* - * Copy data using the GRU. Source or destination can be located in a remote - * partition. - * - * Input: - * dest_gpa destination global physical address - * src_gpa source global physical address - * bytes number of bytes to copy - * - * Output: - * 0 OK - * >0 error - */ -extern int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa, - unsigned int bytes); - -/* - * Reserve GRU resources to be used asynchronously. - * - * input: - * blade_id - blade on which resources should be reserved - * cbrs - number of CBRs - * dsr_bytes - number of DSR bytes needed - * cmp - completion structure for waiting for - * async completions - * output: - * handle to identify resource - * (0 = no resources) - */ -extern unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes, - struct completion *cmp); - -/* - * Release async resources previously reserved. - * - * input: - * han - handle to identify resources - */ -extern void gru_release_async_resources(unsigned long han); - -/* - * Wait for async GRU instructions to complete. - * - * input: - * han - handle to identify resources - */ -extern void gru_wait_async_cbr(unsigned long han); - -/* - * Lock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - * output: - * cb - pointer to first CBR - * dsr - pointer to first DSR - */ -extern void gru_lock_async_resource(unsigned long han, void **cb, void **dsr); - -/* - * Unlock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - */ -extern void gru_unlock_async_resource(unsigned long han); - -#endif /* __GRU_KSERVICES_H_ */ diff --git a/drivers/misc/sgi-gru/grulib.h b/drivers/misc/sgi-gru/grulib.h deleted file mode 100644 index 85c103923632..000000000000 --- a/drivers/misc/sgi-gru/grulib.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __GRULIB_H__ -#define __GRULIB_H__ - -#define GRU_BASENAME "gru" -#define GRU_FULLNAME "/dev/gru" -#define GRU_IOCTL_NUM 'G' - -/* - * Maximum number of GRU segments that a user can have open - * ZZZ temp - set high for testing. Revisit. - */ -#define GRU_MAX_OPEN_CONTEXTS 32 - -/* Set Number of Request Blocks */ -#define GRU_CREATE_CONTEXT _IOWR(GRU_IOCTL_NUM, 1, void *) - -/* Set Context Options */ -#define GRU_SET_CONTEXT_OPTION _IOWR(GRU_IOCTL_NUM, 4, void *) - -/* Fetch exception detail */ -#define GRU_USER_GET_EXCEPTION_DETAIL _IOWR(GRU_IOCTL_NUM, 6, void *) - -/* For user call_os handling - normally a TLB fault */ -#define GRU_USER_CALL_OS _IOWR(GRU_IOCTL_NUM, 8, void *) - -/* For user unload context */ -#define GRU_USER_UNLOAD_CONTEXT _IOWR(GRU_IOCTL_NUM, 9, void *) - -/* For dumpping GRU chiplet state */ -#define GRU_DUMP_CHIPLET_STATE _IOWR(GRU_IOCTL_NUM, 11, void *) - -/* For getting gseg statistics */ -#define GRU_GET_GSEG_STATISTICS _IOWR(GRU_IOCTL_NUM, 12, void *) - -/* For user TLB flushing (primarily for tests) */ -#define GRU_USER_FLUSH_TLB _IOWR(GRU_IOCTL_NUM, 50, void *) - -/* Get some config options (primarily for tests & emulator) */ -#define GRU_GET_CONFIG_INFO _IOWR(GRU_IOCTL_NUM, 51, void *) - -/* Various kernel self-tests */ -#define GRU_KTEST _IOWR(GRU_IOCTL_NUM, 52, void *) - -#define CONTEXT_WINDOW_BYTES(th) (GRU_GSEG_PAGESIZE * (th)) -#define THREAD_POINTER(p, th) (p + GRU_GSEG_PAGESIZE * (th)) -#define GSEG_START(cb) ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1))) - -struct gru_get_gseg_statistics_req { - unsigned long gseg; - struct gru_gseg_statistics stats; -}; - -/* - * Structure used to pass TLB flush parameters to the driver - */ -struct gru_create_context_req { - unsigned long gseg; - unsigned int data_segment_bytes; - unsigned int control_blocks; - unsigned int maximum_thread_count; - unsigned int options; - unsigned char tlb_preload_count; -}; - -/* - * Structure used to pass unload context parameters to the driver - */ -struct gru_unload_context_req { - unsigned long gseg; -}; - -/* - * Structure used to set context options - */ -enum {sco_gseg_owner, sco_cch_req_slice, sco_blade_chiplet}; -struct gru_set_context_option_req { - unsigned long gseg; - int op; - int val0; - long val1; -}; - -/* - * Structure used to pass TLB flush parameters to the driver - */ -struct gru_flush_tlb_req { - unsigned long gseg; - unsigned long vaddr; - size_t len; -}; - -/* - * Structure used to pass TLB flush parameters to the driver - */ -enum {dcs_pid, dcs_gid}; -struct gru_dump_chiplet_state_req { - unsigned int op; - unsigned int gid; - int ctxnum; - char data_opt; - char lock_cch; - char flush_cbrs; - char fill[10]; - pid_t pid; - void *buf; - size_t buflen; - /* ---- output --- */ - unsigned int num_contexts; -}; - -#define GRU_DUMP_MAGIC 0x3474ab6c -struct gru_dump_context_header { - unsigned int magic; - unsigned int gid; - unsigned char ctxnum; - unsigned char cbrcnt; - unsigned char dsrcnt; - pid_t pid; - unsigned long vaddr; - int cch_locked; - unsigned long data[]; -}; - -/* - * GRU configuration info (temp - for testing) - */ -struct gru_config_info { - int cpus; - int blades; - int nodes; - int chiplets; - int fill[16]; -}; - -#endif /* __GRULIB_H__ */ diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c deleted file mode 100644 index 278b76cbd281..000000000000 --- a/drivers/misc/sgi-gru/grumain.c +++ /dev/null @@ -1,969 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * DRIVER TABLE MANAGER + GRU CONTEXT LOAD/UNLOAD - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/spinlock.h> -#include <linux/sched.h> -#include <linux/device.h> -#include <linux/list.h> -#include <linux/err.h> -#include <linux/prefetch.h> -#include <asm/uv/uv_hub.h> -#include "gru.h" -#include "grutables.h" -#include "gruhandles.h" - -unsigned long gru_options __read_mostly; - -static struct device_driver gru_driver = { - .name = "gru" -}; - -static struct device gru_device = { - .init_name = "", - .driver = &gru_driver, -}; - -struct device *grudev = &gru_device; - -/* - * Select a gru fault map to be used by the current cpu. Note that - * multiple cpus may be using the same map. - * ZZZ should be inline but did not work on emulator - */ -int gru_cpu_fault_map_id(void) -{ - int cpu = smp_processor_id(); - int id, core; - - core = uv_cpu_core_number(cpu); - id = core + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu); - return id; -} - -/*--------- ASID Management ------------------------------------------- - * - * Initially, assign asids sequentially from MIN_ASID .. MAX_ASID. - * Once MAX is reached, flush the TLB & start over. However, - * some asids may still be in use. There won't be many (percentage wise) still - * in use. Search active contexts & determine the value of the first - * asid in use ("x"s below). Set "limit" to this value. - * This defines a block of assignable asids. - * - * When "limit" is reached, search forward from limit+1 and determine the - * next block of assignable asids. - * - * Repeat until MAX_ASID is reached, then start over again. - * - * Each time MAX_ASID is reached, increment the asid generation. Since - * the search for in-use asids only checks contexts with GRUs currently - * assigned, asids in some contexts will be missed. Prior to loading - * a context, the asid generation of the GTS asid is rechecked. If it - * doesn't match the current generation, a new asid will be assigned. - * - * 0---------------x------------x---------------------x----| - * ^-next ^-limit ^-MAX_ASID - * - * All asid manipulation & context loading/unloading is protected by the - * gs_lock. - */ - -/* Hit the asid limit. Start over */ -static int gru_wrap_asid(struct gru_state *gru) -{ - gru_dbg(grudev, "gid %d\n", gru->gs_gid); - STAT(asid_wrap); - gru->gs_asid_gen++; - return MIN_ASID; -} - -/* Find the next chunk of unused asids */ -static int gru_reset_asid_limit(struct gru_state *gru, int asid) -{ - int i, gid, inuse_asid, limit; - - gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid); - STAT(asid_next); - limit = MAX_ASID; - if (asid >= limit) - asid = gru_wrap_asid(gru); - gru_flush_all_tlb(gru); - gid = gru->gs_gid; -again: - for (i = 0; i < GRU_NUM_CCH; i++) { - if (!gru->gs_gts[i] || is_kernel_context(gru->gs_gts[i])) - continue; - inuse_asid = gru->gs_gts[i]->ts_gms->ms_asids[gid].mt_asid; - gru_dbg(grudev, "gid %d, gts %p, gms %p, inuse 0x%x, cxt %d\n", - gru->gs_gid, gru->gs_gts[i], gru->gs_gts[i]->ts_gms, - inuse_asid, i); - if (inuse_asid == asid) { - asid += ASID_INC; - if (asid >= limit) { - /* - * empty range: reset the range limit and - * start over - */ - limit = MAX_ASID; - if (asid >= MAX_ASID) - asid = gru_wrap_asid(gru); - goto again; - } - } - - if ((inuse_asid > asid) && (inuse_asid < limit)) - limit = inuse_asid; - } - gru->gs_asid_limit = limit; - gru->gs_asid = asid; - gru_dbg(grudev, "gid %d, new asid 0x%x, new_limit 0x%x\n", gru->gs_gid, - asid, limit); - return asid; -} - -/* Assign a new ASID to a thread context. */ -static int gru_assign_asid(struct gru_state *gru) -{ - int asid; - - gru->gs_asid += ASID_INC; - asid = gru->gs_asid; - if (asid >= gru->gs_asid_limit) - asid = gru_reset_asid_limit(gru, asid); - - gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid); - return asid; -} - -/* - * Clear n bits in a word. Return a word indicating the bits that were cleared. - * Optionally, build an array of chars that contain the bit numbers allocated. - */ -static unsigned long reserve_resources(unsigned long *p, int n, int mmax, - signed char *idx) -{ - unsigned long bits = 0; - int i; - - while (n--) { - i = find_first_bit(p, mmax); - if (i == mmax) - BUG(); - __clear_bit(i, p); - __set_bit(i, &bits); - if (idx) - *idx++ = i; - } - return bits; -} - -unsigned long gru_reserve_cb_resources(struct gru_state *gru, int cbr_au_count, - signed char *cbmap) -{ - return reserve_resources(&gru->gs_cbr_map, cbr_au_count, GRU_CBR_AU, - cbmap); -} - -unsigned long gru_reserve_ds_resources(struct gru_state *gru, int dsr_au_count, - signed char *dsmap) -{ - return reserve_resources(&gru->gs_dsr_map, dsr_au_count, GRU_DSR_AU, - dsmap); -} - -static void reserve_gru_resources(struct gru_state *gru, - struct gru_thread_state *gts) -{ - gru->gs_active_contexts++; - gts->ts_cbr_map = - gru_reserve_cb_resources(gru, gts->ts_cbr_au_count, - gts->ts_cbr_idx); - gts->ts_dsr_map = - gru_reserve_ds_resources(gru, gts->ts_dsr_au_count, NULL); -} - -static void free_gru_resources(struct gru_state *gru, - struct gru_thread_state *gts) -{ - gru->gs_active_contexts--; - gru->gs_cbr_map |= gts->ts_cbr_map; - gru->gs_dsr_map |= gts->ts_dsr_map; -} - -/* - * Check if a GRU has sufficient free resources to satisfy an allocation - * request. Note: GRU locks may or may not be held when this is called. If - * not held, recheck after acquiring the appropriate locks. - * - * Returns 1 if sufficient resources, 0 if not - */ -static int check_gru_resources(struct gru_state *gru, int cbr_au_count, - int dsr_au_count, int max_active_contexts) -{ - return hweight64(gru->gs_cbr_map) >= cbr_au_count - && hweight64(gru->gs_dsr_map) >= dsr_au_count - && gru->gs_active_contexts < max_active_contexts; -} - -/* - * TLB manangment requires tracking all GRU chiplets that have loaded a GSEG - * context. - */ -static int gru_load_mm_tracker(struct gru_state *gru, - struct gru_thread_state *gts) -{ - struct gru_mm_struct *gms = gts->ts_gms; - struct gru_mm_tracker *asids = &gms->ms_asids[gru->gs_gid]; - unsigned short ctxbitmap = (1 << gts->ts_ctxnum); - int asid; - - spin_lock(&gms->ms_asid_lock); - asid = asids->mt_asid; - - spin_lock(&gru->gs_asid_lock); - if (asid == 0 || (asids->mt_ctxbitmap == 0 && asids->mt_asid_gen != - gru->gs_asid_gen)) { - asid = gru_assign_asid(gru); - asids->mt_asid = asid; - asids->mt_asid_gen = gru->gs_asid_gen; - STAT(asid_new); - } else { - STAT(asid_reuse); - } - spin_unlock(&gru->gs_asid_lock); - - BUG_ON(asids->mt_ctxbitmap & ctxbitmap); - asids->mt_ctxbitmap |= ctxbitmap; - if (!test_bit(gru->gs_gid, gms->ms_asidmap)) - __set_bit(gru->gs_gid, gms->ms_asidmap); - spin_unlock(&gms->ms_asid_lock); - - gru_dbg(grudev, - "gid %d, gts %p, gms %p, ctxnum %d, asid 0x%x, asidmap 0x%lx\n", - gru->gs_gid, gts, gms, gts->ts_ctxnum, asid, - gms->ms_asidmap[0]); - return asid; -} - -static void gru_unload_mm_tracker(struct gru_state *gru, - struct gru_thread_state *gts) -{ - struct gru_mm_struct *gms = gts->ts_gms; - struct gru_mm_tracker *asids; - unsigned short ctxbitmap; - - asids = &gms->ms_asids[gru->gs_gid]; - ctxbitmap = (1 << gts->ts_ctxnum); - spin_lock(&gms->ms_asid_lock); - spin_lock(&gru->gs_asid_lock); - BUG_ON((asids->mt_ctxbitmap & ctxbitmap) != ctxbitmap); - asids->mt_ctxbitmap ^= ctxbitmap; - gru_dbg(grudev, "gid %d, gts %p, gms %p, ctxnum %d, asidmap 0x%lx\n", - gru->gs_gid, gts, gms, gts->ts_ctxnum, gms->ms_asidmap[0]); - spin_unlock(&gru->gs_asid_lock); - spin_unlock(&gms->ms_asid_lock); -} - -/* - * Decrement the reference count on a GTS structure. Free the structure - * if the reference count goes to zero. - */ -void gts_drop(struct gru_thread_state *gts) -{ - if (gts && refcount_dec_and_test(>s->ts_refcnt)) { - if (gts->ts_gms) - gru_drop_mmu_notifier(gts->ts_gms); - kfree(gts); - STAT(gts_free); - } -} - -/* - * Locate the GTS structure for the current thread. - */ -static struct gru_thread_state *gru_find_current_gts_nolock(struct gru_vma_data - *vdata, int tsid) -{ - struct gru_thread_state *gts; - - list_for_each_entry(gts, &vdata->vd_head, ts_next) - if (gts->ts_tsid == tsid) - return gts; - return NULL; -} - -/* - * Allocate a thread state structure. - */ -struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, - int cbr_au_count, int dsr_au_count, - unsigned char tlb_preload_count, int options, int tsid) -{ - struct gru_thread_state *gts; - struct gru_mm_struct *gms; - int bytes; - - bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count); - bytes += sizeof(struct gru_thread_state); - gts = kmalloc(bytes, GFP_KERNEL); - if (!gts) - return ERR_PTR(-ENOMEM); - - STAT(gts_alloc); - memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */ - refcount_set(>s->ts_refcnt, 1); - mutex_init(>s->ts_ctxlock); - gts->ts_cbr_au_count = cbr_au_count; - gts->ts_dsr_au_count = dsr_au_count; - gts->ts_tlb_preload_count = tlb_preload_count; - gts->ts_user_options = options; - gts->ts_user_blade_id = -1; - gts->ts_user_chiplet_id = -1; - gts->ts_tsid = tsid; - gts->ts_ctxnum = NULLCTX; - gts->ts_tlb_int_select = -1; - gts->ts_cch_req_slice = -1; - gts->ts_sizeavail = GRU_SIZEAVAIL(PAGE_SHIFT); - if (vma) { - gts->ts_mm = current->mm; - gts->ts_vma = vma; - gms = gru_register_mmu_notifier(); - if (IS_ERR(gms)) - goto err; - gts->ts_gms = gms; - } - - gru_dbg(grudev, "alloc gts %p\n", gts); - return gts; - -err: - gts_drop(gts); - return ERR_CAST(gms); -} - -/* - * Allocate a vma private data structure. - */ -struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, int tsid) -{ - struct gru_vma_data *vdata = NULL; - - vdata = kmalloc_obj(*vdata); - if (!vdata) - return NULL; - - STAT(vdata_alloc); - INIT_LIST_HEAD(&vdata->vd_head); - spin_lock_init(&vdata->vd_lock); - gru_dbg(grudev, "alloc vdata %p\n", vdata); - return vdata; -} - -/* - * Find the thread state structure for the current thread. - */ -struct gru_thread_state *gru_find_thread_state(struct vm_area_struct *vma, - int tsid) -{ - struct gru_vma_data *vdata = vma->vm_private_data; - struct gru_thread_state *gts; - - spin_lock(&vdata->vd_lock); - gts = gru_find_current_gts_nolock(vdata, tsid); - spin_unlock(&vdata->vd_lock); - gru_dbg(grudev, "vma %p, gts %p\n", vma, gts); - return gts; -} - -/* - * Allocate a new thread state for a GSEG. Note that races may allow - * another thread to race to create a gts. - */ -struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma, - int tsid) -{ - struct gru_vma_data *vdata = vma->vm_private_data; - struct gru_thread_state *gts, *ngts; - - gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count, - vdata->vd_dsr_au_count, - vdata->vd_tlb_preload_count, - vdata->vd_user_options, tsid); - if (IS_ERR(gts)) - return gts; - - spin_lock(&vdata->vd_lock); - ngts = gru_find_current_gts_nolock(vdata, tsid); - if (ngts) { - gts_drop(gts); - gts = ngts; - STAT(gts_double_allocate); - } else { - list_add(>s->ts_next, &vdata->vd_head); - } - spin_unlock(&vdata->vd_lock); - gru_dbg(grudev, "vma %p, gts %p\n", vma, gts); - return gts; -} - -/* - * Free the GRU context assigned to the thread state. - */ -static void gru_free_gru_context(struct gru_thread_state *gts) -{ - struct gru_state *gru; - - gru = gts->ts_gru; - gru_dbg(grudev, "gts %p, gid %d\n", gts, gru->gs_gid); - - spin_lock(&gru->gs_lock); - gru->gs_gts[gts->ts_ctxnum] = NULL; - free_gru_resources(gru, gts); - BUG_ON(test_bit(gts->ts_ctxnum, &gru->gs_context_map) == 0); - __clear_bit(gts->ts_ctxnum, &gru->gs_context_map); - gts->ts_ctxnum = NULLCTX; - gts->ts_gru = NULL; - gts->ts_blade = -1; - spin_unlock(&gru->gs_lock); - - gts_drop(gts); - STAT(free_context); -} - -/* - * Prefetching cachelines help hardware performance. - * (Strictly a performance enhancement. Not functionally required). - */ -static void prefetch_data(void *p, int num, int stride) -{ - while (num-- > 0) { - prefetchw(p); - p += stride; - } -} - -static inline long gru_copy_handle(void *d, void *s) -{ - memcpy(d, s, GRU_HANDLE_BYTES); - return GRU_HANDLE_BYTES; -} - -static void gru_prefetch_context(void *gseg, void *cb, void *cbe, - unsigned long cbrmap, unsigned long length) -{ - int i, scr; - - prefetch_data(gseg + GRU_DS_BASE, length / GRU_CACHE_LINE_BYTES, - GRU_CACHE_LINE_BYTES); - - for_each_cbr_in_allocation_map(i, &cbrmap, scr) { - prefetch_data(cb, 1, GRU_CACHE_LINE_BYTES); - prefetch_data(cbe + i * GRU_HANDLE_STRIDE, 1, - GRU_CACHE_LINE_BYTES); - cb += GRU_HANDLE_STRIDE; - } -} - -static void gru_load_context_data(void *save, void *grubase, int ctxnum, - unsigned long cbrmap, unsigned long dsrmap, - int data_valid) -{ - void *gseg, *cb, *cbe; - unsigned long length; - int i, scr; - - gseg = grubase + ctxnum * GRU_GSEG_STRIDE; - cb = gseg + GRU_CB_BASE; - cbe = grubase + GRU_CBE_BASE; - length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; - gru_prefetch_context(gseg, cb, cbe, cbrmap, length); - - for_each_cbr_in_allocation_map(i, &cbrmap, scr) { - if (data_valid) { - save += gru_copy_handle(cb, save); - save += gru_copy_handle(cbe + i * GRU_HANDLE_STRIDE, - save); - } else { - memset(cb, 0, GRU_CACHE_LINE_BYTES); - memset(cbe + i * GRU_HANDLE_STRIDE, 0, - GRU_CACHE_LINE_BYTES); - } - /* Flush CBE to hide race in context restart */ - mb(); - gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); - cb += GRU_HANDLE_STRIDE; - } - - if (data_valid) - memcpy(gseg + GRU_DS_BASE, save, length); - else - memset(gseg + GRU_DS_BASE, 0, length); -} - -static void gru_unload_context_data(void *save, void *grubase, int ctxnum, - unsigned long cbrmap, unsigned long dsrmap) -{ - void *gseg, *cb, *cbe; - unsigned long length; - int i, scr; - - gseg = grubase + ctxnum * GRU_GSEG_STRIDE; - cb = gseg + GRU_CB_BASE; - cbe = grubase + GRU_CBE_BASE; - length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; - - /* CBEs may not be coherent. Flush them from cache */ - for_each_cbr_in_allocation_map(i, &cbrmap, scr) - gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); - mb(); /* Let the CL flush complete */ - - gru_prefetch_context(gseg, cb, cbe, cbrmap, length); - - for_each_cbr_in_allocation_map(i, &cbrmap, scr) { - save += gru_copy_handle(save, cb); - save += gru_copy_handle(save, cbe + i * GRU_HANDLE_STRIDE); - cb += GRU_HANDLE_STRIDE; - } - memcpy(save, gseg + GRU_DS_BASE, length); -} - -void gru_unload_context(struct gru_thread_state *gts, int savestate) -{ - struct gru_state *gru = gts->ts_gru; - struct gru_context_configuration_handle *cch; - int ctxnum = gts->ts_ctxnum; - - if (!is_kernel_context(gts)) - zap_special_vma_range(gts->ts_vma, UGRUADDR(gts), GRU_GSEG_PAGESIZE); - cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); - - gru_dbg(grudev, "gts %p, cbrmap 0x%lx, dsrmap 0x%lx\n", - gts, gts->ts_cbr_map, gts->ts_dsr_map); - lock_cch_handle(cch); - if (cch_interrupt_sync(cch)) - BUG(); - - if (!is_kernel_context(gts)) - gru_unload_mm_tracker(gru, gts); - if (savestate) { - gru_unload_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr, - ctxnum, gts->ts_cbr_map, - gts->ts_dsr_map); - gts->ts_data_valid = 1; - } - - if (cch_deallocate(cch)) - BUG(); - unlock_cch_handle(cch); - - gru_free_gru_context(gts); -} - -/* - * Load a GRU context by copying it from the thread data structure in memory - * to the GRU. - */ -void gru_load_context(struct gru_thread_state *gts) -{ - struct gru_state *gru = gts->ts_gru; - struct gru_context_configuration_handle *cch; - int i, err, asid, ctxnum = gts->ts_ctxnum; - - cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); - lock_cch_handle(cch); - cch->tfm_fault_bit_enable = - (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL - || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); - cch->tlb_int_enable = (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); - if (cch->tlb_int_enable) { - gts->ts_tlb_int_select = gru_cpu_fault_map_id(); - cch->tlb_int_select = gts->ts_tlb_int_select; - } - if (gts->ts_cch_req_slice >= 0) { - cch->req_slice_set_enable = 1; - cch->req_slice = gts->ts_cch_req_slice; - } else { - cch->req_slice_set_enable =0; - } - cch->tfm_done_bit_enable = 0; - cch->dsr_allocation_map = gts->ts_dsr_map; - cch->cbr_allocation_map = gts->ts_cbr_map; - - if (is_kernel_context(gts)) { - cch->unmap_enable = 1; - cch->tfm_done_bit_enable = 1; - cch->cb_int_enable = 1; - cch->tlb_int_select = 0; /* For now, ints go to cpu 0 */ - } else { - cch->unmap_enable = 0; - cch->tfm_done_bit_enable = 0; - cch->cb_int_enable = 0; - asid = gru_load_mm_tracker(gru, gts); - for (i = 0; i < 8; i++) { - cch->asid[i] = asid + i; - cch->sizeavail[i] = gts->ts_sizeavail; - } - } - - err = cch_allocate(cch); - if (err) { - gru_dbg(grudev, - "err %d: cch %p, gts %p, cbr 0x%lx, dsr 0x%lx\n", - err, cch, gts, gts->ts_cbr_map, gts->ts_dsr_map); - BUG(); - } - - gru_load_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr, ctxnum, - gts->ts_cbr_map, gts->ts_dsr_map, gts->ts_data_valid); - - if (cch_start(cch)) - BUG(); - unlock_cch_handle(cch); - - gru_dbg(grudev, "gid %d, gts %p, cbrmap 0x%lx, dsrmap 0x%lx, tie %d, tis %d\n", - gts->ts_gru->gs_gid, gts, gts->ts_cbr_map, gts->ts_dsr_map, - (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR), gts->ts_tlb_int_select); -} - -/* - * Update fields in an active CCH: - * - retarget interrupts on local blade - * - update sizeavail mask - */ -int gru_update_cch(struct gru_thread_state *gts) -{ - struct gru_context_configuration_handle *cch; - struct gru_state *gru = gts->ts_gru; - int i, ctxnum = gts->ts_ctxnum, ret = 0; - - cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); - - lock_cch_handle(cch); - if (cch->state == CCHSTATE_ACTIVE) { - if (gru->gs_gts[gts->ts_ctxnum] != gts) - goto exit; - if (cch_interrupt(cch)) - BUG(); - for (i = 0; i < 8; i++) - cch->sizeavail[i] = gts->ts_sizeavail; - gts->ts_tlb_int_select = gru_cpu_fault_map_id(); - cch->tlb_int_select = gru_cpu_fault_map_id(); - cch->tfm_fault_bit_enable = - (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL - || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); - if (cch_start(cch)) - BUG(); - ret = 1; - } -exit: - unlock_cch_handle(cch); - return ret; -} - -/* - * Update CCH tlb interrupt select. Required when all the following is true: - * - task's GRU context is loaded into a GRU - * - task is using interrupt notification for TLB faults - * - task has migrated to a different cpu on the same blade where - * it was previously running. - */ -static int gru_retarget_intr(struct gru_thread_state *gts) -{ - if (gts->ts_tlb_int_select < 0 - || gts->ts_tlb_int_select == gru_cpu_fault_map_id()) - return 0; - - gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select, - gru_cpu_fault_map_id()); - return gru_update_cch(gts); -} - -/* - * Check if a GRU context is allowed to use a specific chiplet. By default - * a context is assigned to any blade-local chiplet. However, users can - * override this. - * Returns 1 if assignment allowed, 0 otherwise - */ -static int gru_check_chiplet_assignment(struct gru_state *gru, - struct gru_thread_state *gts) -{ - int blade_id; - int chiplet_id; - - blade_id = gts->ts_user_blade_id; - if (blade_id < 0) - blade_id = uv_numa_blade_id(); - - chiplet_id = gts->ts_user_chiplet_id; - return gru->gs_blade_id == blade_id && - (chiplet_id < 0 || chiplet_id == gru->gs_chiplet_id); -} - -/* - * Unload the gru context if it is not assigned to the correct blade or - * chiplet. Misassignment can occur if the process migrates to a different - * blade or if the user changes the selected blade/chiplet. - */ -int gru_check_context_placement(struct gru_thread_state *gts) -{ - struct gru_state *gru; - int ret = 0; - - /* - * If the current task is the context owner, verify that the - * context is correctly placed. This test is skipped for non-owner - * references. Pthread apps use non-owner references to the CBRs. - */ - gru = gts->ts_gru; - /* - * If gru or gts->ts_tgid_owner isn't initialized properly, return - * success to indicate that the caller does not need to unload the - * gru context.The caller is responsible for their inspection and - * reinitialization if needed. - */ - if (!gru || gts->ts_tgid_owner != current->tgid) - return ret; - - if (!gru_check_chiplet_assignment(gru, gts)) { - STAT(check_context_unload); - ret = -EINVAL; - } else if (gru_retarget_intr(gts)) { - STAT(check_context_retarget_intr); - } - - return ret; -} - - -/* - * Insufficient GRU resources available on the local blade. Steal a context from - * a process. This is a hack until a _real_ resource scheduler is written.... - */ -#define next_ctxnum(n) ((n) < GRU_NUM_CCH - 2 ? (n) + 1 : 0) -#define next_gru(b, g) (((g) < &(b)->bs_grus[GRU_CHIPLETS_PER_BLADE - 1]) ? \ - ((g)+1) : &(b)->bs_grus[0]) - -static int is_gts_stealable(struct gru_thread_state *gts, - struct gru_blade_state *bs) -{ - if (is_kernel_context(gts)) - return down_write_trylock(&bs->bs_kgts_sema); - else - return mutex_trylock(>s->ts_ctxlock); -} - -static void gts_stolen(struct gru_thread_state *gts, - struct gru_blade_state *bs) -{ - if (is_kernel_context(gts)) { - up_write(&bs->bs_kgts_sema); - STAT(steal_kernel_context); - } else { - mutex_unlock(>s->ts_ctxlock); - STAT(steal_user_context); - } -} - -void gru_steal_context(struct gru_thread_state *gts) -{ - struct gru_blade_state *blade; - struct gru_state *gru, *gru0; - struct gru_thread_state *ngts = NULL; - int ctxnum, ctxnum0, flag = 0, cbr, dsr; - int blade_id; - - blade_id = gts->ts_user_blade_id; - if (blade_id < 0) - blade_id = uv_numa_blade_id(); - cbr = gts->ts_cbr_au_count; - dsr = gts->ts_dsr_au_count; - - blade = gru_base[blade_id]; - spin_lock(&blade->bs_lock); - - ctxnum = next_ctxnum(blade->bs_lru_ctxnum); - gru = blade->bs_lru_gru; - if (ctxnum == 0) - gru = next_gru(blade, gru); - blade->bs_lru_gru = gru; - blade->bs_lru_ctxnum = ctxnum; - ctxnum0 = ctxnum; - gru0 = gru; - while (1) { - if (gru_check_chiplet_assignment(gru, gts)) { - if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH)) - break; - spin_lock(&gru->gs_lock); - for (; ctxnum < GRU_NUM_CCH; ctxnum++) { - if (flag && gru == gru0 && ctxnum == ctxnum0) - break; - ngts = gru->gs_gts[ctxnum]; - /* - * We are grabbing locks out of order, so trylock is - * needed. GTSs are usually not locked, so the odds of - * success are high. If trylock fails, try to steal a - * different GSEG. - */ - if (ngts && is_gts_stealable(ngts, blade)) - break; - ngts = NULL; - } - spin_unlock(&gru->gs_lock); - if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0)) - break; - } - if (flag && gru == gru0) - break; - flag = 1; - ctxnum = 0; - gru = next_gru(blade, gru); - } - spin_unlock(&blade->bs_lock); - - if (ngts) { - gts->ustats.context_stolen++; - ngts->ts_steal_jiffies = jiffies; - gru_unload_context(ngts, is_kernel_context(ngts) ? 0 : 1); - gts_stolen(ngts, blade); - } else { - STAT(steal_context_failed); - } - gru_dbg(grudev, - "stole gid %d, ctxnum %d from gts %p. Need cb %d, ds %d;" - " avail cb %ld, ds %ld\n", - gru->gs_gid, ctxnum, ngts, cbr, dsr, hweight64(gru->gs_cbr_map), - hweight64(gru->gs_dsr_map)); -} - -/* - * Assign a gru context. - */ -static int gru_assign_context_number(struct gru_state *gru) -{ - int ctxnum; - - ctxnum = find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH); - __set_bit(ctxnum, &gru->gs_context_map); - return ctxnum; -} - -/* - * Scan the GRUs on the local blade & assign a GRU context. - */ -struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts) -{ - struct gru_state *gru, *grux; - int i, max_active_contexts; - int blade_id = gts->ts_user_blade_id; - - if (blade_id < 0) - blade_id = uv_numa_blade_id(); -again: - gru = NULL; - max_active_contexts = GRU_NUM_CCH; - for_each_gru_on_blade(grux, blade_id, i) { - if (!gru_check_chiplet_assignment(grux, gts)) - continue; - if (check_gru_resources(grux, gts->ts_cbr_au_count, - gts->ts_dsr_au_count, - max_active_contexts)) { - gru = grux; - max_active_contexts = grux->gs_active_contexts; - if (max_active_contexts == 0) - break; - } - } - - if (gru) { - spin_lock(&gru->gs_lock); - if (!check_gru_resources(gru, gts->ts_cbr_au_count, - gts->ts_dsr_au_count, GRU_NUM_CCH)) { - spin_unlock(&gru->gs_lock); - goto again; - } - reserve_gru_resources(gru, gts); - gts->ts_gru = gru; - gts->ts_blade = gru->gs_blade_id; - gts->ts_ctxnum = gru_assign_context_number(gru); - refcount_inc(>s->ts_refcnt); - gru->gs_gts[gts->ts_ctxnum] = gts; - spin_unlock(&gru->gs_lock); - - STAT(assign_context); - gru_dbg(grudev, - "gseg %p, gts %p, gid %d, ctx %d, cbr %d, dsr %d\n", - gseg_virtual_address(gts->ts_gru, gts->ts_ctxnum), gts, - gts->ts_gru->gs_gid, gts->ts_ctxnum, - gts->ts_cbr_au_count, gts->ts_dsr_au_count); - } else { - gru_dbg(grudev, "failed to allocate a GTS %s\n", ""); - STAT(assign_context_failed); - } - - return gru; -} - -/* - * gru_nopage - * - * Map the user's GRU segment - * - * Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries. - */ -vm_fault_t gru_fault(struct vm_fault *vmf) -{ - struct vm_area_struct *vma = vmf->vma; - struct gru_thread_state *gts; - unsigned long paddr, vaddr; - unsigned long expires; - - vaddr = vmf->address; - gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n", - vma, vaddr, GSEG_BASE(vaddr)); - STAT(nopfn); - - /* The following check ensures vaddr is a valid address in the VMA */ - gts = gru_find_thread_state(vma, TSID(vaddr, vma)); - if (!gts) - return VM_FAULT_SIGBUS; - -again: - mutex_lock(>s->ts_ctxlock); - - if (gru_check_context_placement(gts)) { - mutex_unlock(>s->ts_ctxlock); - gru_unload_context(gts, 1); - return VM_FAULT_NOPAGE; - } - - if (!gts->ts_gru) { - STAT(load_user_context); - if (!gru_assign_gru_context(gts)) { - mutex_unlock(>s->ts_ctxlock); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(GRU_ASSIGN_DELAY); /* true hack ZZZ */ - expires = gts->ts_steal_jiffies + GRU_STEAL_DELAY; - if (time_before(expires, jiffies)) - gru_steal_context(gts); - goto again; - } - gru_load_context(gts); - paddr = gseg_physical_address(gts->ts_gru, gts->ts_ctxnum); - remap_pfn_range(vma, vaddr & ~(GRU_GSEG_PAGESIZE - 1), - paddr >> PAGE_SHIFT, GRU_GSEG_PAGESIZE, - vma->vm_page_prot); - } - - mutex_unlock(>s->ts_ctxlock); - - return VM_FAULT_NOPAGE; -} - diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c deleted file mode 100644 index 97b8b38ab47d..000000000000 --- a/drivers/misc/sgi-gru/gruprocfs.c +++ /dev/null @@ -1,308 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * PROC INTERFACES - * - * This file supports the /proc interfaces for the GRU driver - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include <linux/proc_fs.h> -#include <linux/device.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> -#include "gru.h" -#include "grulib.h" -#include "grutables.h" - -#define printstat(s, f) printstat_val(s, &gru_stats.f, #f) - -static void printstat_val(struct seq_file *s, atomic_long_t *v, char *id) -{ - unsigned long val = atomic_long_read(v); - - seq_printf(s, "%16lu %s\n", val, id); -} - -static int statistics_show(struct seq_file *s, void *p) -{ - printstat(s, vdata_alloc); - printstat(s, vdata_free); - printstat(s, gts_alloc); - printstat(s, gts_free); - printstat(s, gms_alloc); - printstat(s, gms_free); - printstat(s, gts_double_allocate); - printstat(s, assign_context); - printstat(s, assign_context_failed); - printstat(s, free_context); - printstat(s, load_user_context); - printstat(s, load_kernel_context); - printstat(s, lock_kernel_context); - printstat(s, unlock_kernel_context); - printstat(s, steal_user_context); - printstat(s, steal_kernel_context); - printstat(s, steal_context_failed); - printstat(s, nopfn); - printstat(s, asid_new); - printstat(s, asid_next); - printstat(s, asid_wrap); - printstat(s, asid_reuse); - printstat(s, intr); - printstat(s, intr_cbr); - printstat(s, intr_tfh); - printstat(s, intr_spurious); - printstat(s, intr_mm_lock_failed); - printstat(s, call_os); - printstat(s, call_os_wait_queue); - printstat(s, user_flush_tlb); - printstat(s, user_unload_context); - printstat(s, user_exception); - printstat(s, set_context_option); - printstat(s, check_context_retarget_intr); - printstat(s, check_context_unload); - printstat(s, tlb_dropin); - printstat(s, tlb_preload_page); - printstat(s, tlb_dropin_fail_no_asid); - printstat(s, tlb_dropin_fail_upm); - printstat(s, tlb_dropin_fail_invalid); - printstat(s, tlb_dropin_fail_range_active); - printstat(s, tlb_dropin_fail_idle); - printstat(s, tlb_dropin_fail_fmm); - printstat(s, tlb_dropin_fail_no_exception); - printstat(s, tfh_stale_on_fault); - printstat(s, mmu_invalidate_range); - printstat(s, mmu_invalidate_page); - printstat(s, flush_tlb); - printstat(s, flush_tlb_gru); - printstat(s, flush_tlb_gru_tgh); - printstat(s, flush_tlb_gru_zero_asid); - printstat(s, copy_gpa); - printstat(s, read_gpa); - printstat(s, mesq_receive); - printstat(s, mesq_receive_none); - printstat(s, mesq_send); - printstat(s, mesq_send_failed); - printstat(s, mesq_noop); - printstat(s, mesq_send_unexpected_error); - printstat(s, mesq_send_lb_overflow); - printstat(s, mesq_send_qlimit_reached); - printstat(s, mesq_send_amo_nacked); - printstat(s, mesq_send_put_nacked); - printstat(s, mesq_qf_locked); - printstat(s, mesq_qf_noop_not_full); - printstat(s, mesq_qf_switch_head_failed); - printstat(s, mesq_qf_unexpected_error); - printstat(s, mesq_noop_unexpected_error); - printstat(s, mesq_noop_lb_overflow); - printstat(s, mesq_noop_qlimit_reached); - printstat(s, mesq_noop_amo_nacked); - printstat(s, mesq_noop_put_nacked); - printstat(s, mesq_noop_page_overflow); - return 0; -} - -static ssize_t statistics_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *data) -{ - memset(&gru_stats, 0, sizeof(gru_stats)); - return count; -} - -static int mcs_statistics_show(struct seq_file *s, void *p) -{ - int op; - unsigned long total, count, max; - static char *id[] = {"cch_allocate", "cch_start", "cch_interrupt", - "cch_interrupt_sync", "cch_deallocate", "tfh_write_only", - "tfh_write_restart", "tgh_invalidate"}; - - seq_puts(s, "#id count aver-clks max-clks\n"); - for (op = 0; op < mcsop_last; op++) { - count = atomic_long_read(&mcs_op_statistics[op].count); - total = atomic_long_read(&mcs_op_statistics[op].total); - max = mcs_op_statistics[op].max; - seq_printf(s, "%-20s%12ld%12ld%12ld\n", id[op], count, - count ? total / count : 0, max); - } - return 0; -} - -static ssize_t mcs_statistics_write(struct file *file, - const char __user *userbuf, size_t count, loff_t *data) -{ - memset(mcs_op_statistics, 0, sizeof(mcs_op_statistics)); - return count; -} - -static int options_show(struct seq_file *s, void *p) -{ - seq_printf(s, "#bitmask: 1=trace, 2=statistics\n"); - seq_printf(s, "0x%lx\n", gru_options); - return 0; -} - -static ssize_t options_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *data) -{ - int ret; - - ret = kstrtoul_from_user(userbuf, count, 0, &gru_options); - if (ret) - return ret; - - return count; -} - -static int cch_seq_show(struct seq_file *file, void *data) -{ - long gid = *(long *)data; - int i; - struct gru_state *gru = GID_TO_GRU(gid); - struct gru_thread_state *ts; - const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" }; - - if (gid == 0) - seq_puts(file, "# gid bid ctx# asid pid cbrs dsbytes mode\n"); - if (gru) - for (i = 0; i < GRU_NUM_CCH; i++) { - ts = gru->gs_gts[i]; - if (!ts) - continue; - seq_printf(file, " %5d%5d%6d%7d%9d%6d%8d%8s\n", - gru->gs_gid, gru->gs_blade_id, i, - is_kernel_context(ts) ? 0 : ts->ts_gms->ms_asids[gid].mt_asid, - is_kernel_context(ts) ? 0 : ts->ts_tgid_owner, - ts->ts_cbr_au_count * GRU_CBR_AU_SIZE, - ts->ts_cbr_au_count * GRU_DSR_AU_BYTES, - mode[ts->ts_user_options & - GRU_OPT_MISS_MASK]); - } - - return 0; -} - -static int gru_seq_show(struct seq_file *file, void *data) -{ - long gid = *(long *)data, ctxfree, cbrfree, dsrfree; - struct gru_state *gru = GID_TO_GRU(gid); - - if (gid == 0) { - seq_puts(file, "# gid nid ctx cbr dsr ctx cbr dsr\n"); - seq_puts(file, "# busy busy busy free free free\n"); - } - if (gru) { - ctxfree = GRU_NUM_CCH - gru->gs_active_contexts; - cbrfree = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; - dsrfree = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; - seq_printf(file, " %5d%5d%7ld%6ld%6ld%8ld%6ld%6ld\n", - gru->gs_gid, gru->gs_blade_id, GRU_NUM_CCH - ctxfree, - GRU_NUM_CBE - cbrfree, GRU_NUM_DSR_BYTES - dsrfree, - ctxfree, cbrfree, dsrfree); - } - - return 0; -} - -static void seq_stop(struct seq_file *file, void *data) -{ -} - -static void *seq_start(struct seq_file *file, loff_t *gid) -{ - if (*gid < gru_max_gids) - return gid; - return NULL; -} - -static void *seq_next(struct seq_file *file, void *data, loff_t *gid) -{ - (*gid)++; - if (*gid < gru_max_gids) - return gid; - return NULL; -} - -static const struct seq_operations cch_seq_ops = { - .start = seq_start, - .next = seq_next, - .stop = seq_stop, - .show = cch_seq_show -}; - -static const struct seq_operations gru_seq_ops = { - .start = seq_start, - .next = seq_next, - .stop = seq_stop, - .show = gru_seq_show -}; - -static int statistics_open(struct inode *inode, struct file *file) -{ - return single_open(file, statistics_show, NULL); -} - -static int mcs_statistics_open(struct inode *inode, struct file *file) -{ - return single_open(file, mcs_statistics_show, NULL); -} - -static int options_open(struct inode *inode, struct file *file) -{ - return single_open(file, options_show, NULL); -} - -/* *INDENT-OFF* */ -static const struct proc_ops statistics_proc_ops = { - .proc_open = statistics_open, - .proc_read = seq_read, - .proc_write = statistics_write, - .proc_lseek = seq_lseek, - .proc_release = single_release, -}; - -static const struct proc_ops mcs_statistics_proc_ops = { - .proc_open = mcs_statistics_open, - .proc_read = seq_read, - .proc_write = mcs_statistics_write, - .proc_lseek = seq_lseek, - .proc_release = single_release, -}; - -static const struct proc_ops options_proc_ops = { - .proc_open = options_open, - .proc_read = seq_read, - .proc_write = options_write, - .proc_lseek = seq_lseek, - .proc_release = single_release, -}; - -static struct proc_dir_entry *proc_gru __read_mostly; - -int gru_proc_init(void) -{ - proc_gru = proc_mkdir("sgi_uv/gru", NULL); - if (!proc_gru) - return -1; - if (!proc_create("statistics", 0644, proc_gru, &statistics_proc_ops)) - goto err; - if (!proc_create("mcs_statistics", 0644, proc_gru, &mcs_statistics_proc_ops)) - goto err; - if (!proc_create("debug_options", 0644, proc_gru, &options_proc_ops)) - goto err; - if (!proc_create_seq("cch_status", 0444, proc_gru, &cch_seq_ops)) - goto err; - if (!proc_create_seq("gru_status", 0444, proc_gru, &gru_seq_ops)) - goto err; - return 0; -err: - remove_proc_subtree("sgi_uv/gru", NULL); - return -1; -} - -void gru_proc_exit(void) -{ - remove_proc_subtree("sgi_uv/gru", NULL); -} diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h deleted file mode 100644 index 640daf1994df..000000000000 --- a/drivers/misc/sgi-gru/grutables.h +++ /dev/null @@ -1,659 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SN Platform GRU Driver - * - * GRU DRIVER TABLES, MACROS, externs, etc - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#ifndef __GRUTABLES_H__ -#define __GRUTABLES_H__ - -/* - * GRU Chiplet: - * The GRU is a user addressible memory accelerator. It provides - * several forms of load, store, memset, bcopy instructions. In addition, it - * contains special instructions for AMOs, sending messages to message - * queues, etc. - * - * The GRU is an integral part of the node controller. It connects - * directly to the cpu socket. In its current implementation, there are 2 - * GRU chiplets in the node controller on each blade (~node). - * - * The entire GRU memory space is fully coherent and cacheable by the cpus. - * - * Each GRU chiplet has a physical memory map that looks like the following: - * - * +-----------------+ - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * +-----------------+ - * | system control | - * +-----------------+ _______ +-------------+ - * |/////////////////| / | | - * |/////////////////| / | | - * |/////////////////| / | instructions| - * |/////////////////| / | | - * |/////////////////| / | | - * |/////////////////| / |-------------| - * |/////////////////| / | | - * +-----------------+ | | - * | context 15 | | data | - * +-----------------+ | | - * | ...... | \ | | - * +-----------------+ \____________ +-------------+ - * | context 1 | - * +-----------------+ - * | context 0 | - * +-----------------+ - * - * Each of the "contexts" is a chunk of memory that can be mmaped into user - * space. The context consists of 2 parts: - * - * - an instruction space that can be directly accessed by the user - * to issue GRU instructions and to check instruction status. - * - * - a data area that acts as normal RAM. - * - * User instructions contain virtual addresses of data to be accessed by the - * GRU. The GRU contains a TLB that is used to convert these user virtual - * addresses to physical addresses. - * - * The "system control" area of the GRU chiplet is used by the kernel driver - * to manage user contexts and to perform functions such as TLB dropin and - * purging. - * - * One context may be reserved for the kernel and used for cross-partition - * communication. The GRU will also be used to asynchronously zero out - * large blocks of memory (not currently implemented). - * - * - * Tables: - * - * VDATA-VMA Data - Holds a few parameters. Head of linked list of - * GTS tables for threads using the GSEG - * GTS - Gru Thread State - contains info for managing a GSEG context. A - * GTS is allocated for each thread accessing a - * GSEG. - * GTD - GRU Thread Data - contains shadow copy of GRU data when GSEG is - * not loaded into a GRU - * GMS - GRU Memory Struct - Used to manage TLB shootdowns. Tracks GRUs - * where a GSEG has been loaded. Similar to - * an mm_struct but for GRU. - * - * GS - GRU State - Used to manage the state of a GRU chiplet - * BS - Blade State - Used to manage state of all GRU chiplets - * on a blade - * - * - * Normal task tables for task using GRU. - * - 2 threads in process - * - 2 GSEGs open in process - * - GSEG1 is being used by both threads - * - GSEG2 is used only by thread 2 - * - * task -->| - * task ---+---> mm ->------ (notifier) -------+-> gms - * | | - * |--> vma -> vdata ---> gts--->| GSEG1 (thread1) - * | | | - * | +-> gts--->| GSEG1 (thread2) - * | | - * |--> vma -> vdata ---> gts--->| GSEG2 (thread2) - * . - * . - * - * GSEGs are marked DONTCOPY on fork - * - * At open - * file.private_data -> NULL - * - * At mmap, - * vma -> vdata - * - * After gseg reference - * vma -> vdata ->gts - * - * After fork - * parent - * vma -> vdata -> gts - * child - * (vma is not copied) - * - */ - -#include <linux/refcount.h> -#include <linux/rmap.h> -#include <linux/interrupt.h> -#include <linux/mutex.h> -#include <linux/wait.h> -#include <linux/mmu_notifier.h> -#include <linux/mm_types.h> -#include "gru.h" -#include "grulib.h" -#include "gruhandles.h" - -extern struct gru_stats_s gru_stats; -extern struct gru_blade_state *gru_base[]; -extern unsigned long gru_start_paddr, gru_end_paddr; -extern void *gru_start_vaddr; -extern unsigned int gru_max_gids; - -#define GRU_MAX_BLADES MAX_NUMNODES -#define GRU_MAX_GRUS (GRU_MAX_BLADES * GRU_CHIPLETS_PER_BLADE) - -#define GRU_DRIVER_ID_STR "SGI GRU Device Driver" -#define GRU_DRIVER_VERSION_STR "0.85" - -/* - * GRU statistics. - */ -struct gru_stats_s { - atomic_long_t vdata_alloc; - atomic_long_t vdata_free; - atomic_long_t gts_alloc; - atomic_long_t gts_free; - atomic_long_t gms_alloc; - atomic_long_t gms_free; - atomic_long_t gts_double_allocate; - atomic_long_t assign_context; - atomic_long_t assign_context_failed; - atomic_long_t free_context; - atomic_long_t load_user_context; - atomic_long_t load_kernel_context; - atomic_long_t lock_kernel_context; - atomic_long_t unlock_kernel_context; - atomic_long_t steal_user_context; - atomic_long_t steal_kernel_context; - atomic_long_t steal_context_failed; - atomic_long_t nopfn; - atomic_long_t asid_new; - atomic_long_t asid_next; - atomic_long_t asid_wrap; - atomic_long_t asid_reuse; - atomic_long_t intr; - atomic_long_t intr_cbr; - atomic_long_t intr_tfh; - atomic_long_t intr_spurious; - atomic_long_t intr_mm_lock_failed; - atomic_long_t call_os; - atomic_long_t call_os_wait_queue; - atomic_long_t user_flush_tlb; - atomic_long_t user_unload_context; - atomic_long_t user_exception; - atomic_long_t set_context_option; - atomic_long_t check_context_retarget_intr; - atomic_long_t check_context_unload; - atomic_long_t tlb_dropin; - atomic_long_t tlb_preload_page; - atomic_long_t tlb_dropin_fail_no_asid; - atomic_long_t tlb_dropin_fail_upm; - atomic_long_t tlb_dropin_fail_invalid; - atomic_long_t tlb_dropin_fail_range_active; - atomic_long_t tlb_dropin_fail_idle; - atomic_long_t tlb_dropin_fail_fmm; - atomic_long_t tlb_dropin_fail_no_exception; - atomic_long_t tfh_stale_on_fault; - atomic_long_t mmu_invalidate_range; - atomic_long_t mmu_invalidate_page; - atomic_long_t flush_tlb; - atomic_long_t flush_tlb_gru; - atomic_long_t flush_tlb_gru_tgh; - atomic_long_t flush_tlb_gru_zero_asid; - - atomic_long_t copy_gpa; - atomic_long_t read_gpa; - - atomic_long_t mesq_receive; - atomic_long_t mesq_receive_none; - atomic_long_t mesq_send; - atomic_long_t mesq_send_failed; - atomic_long_t mesq_noop; - atomic_long_t mesq_send_unexpected_error; - atomic_long_t mesq_send_lb_overflow; - atomic_long_t mesq_send_qlimit_reached; - atomic_long_t mesq_send_amo_nacked; - atomic_long_t mesq_send_put_nacked; - atomic_long_t mesq_page_overflow; - atomic_long_t mesq_qf_locked; - atomic_long_t mesq_qf_noop_not_full; - atomic_long_t mesq_qf_switch_head_failed; - atomic_long_t mesq_qf_unexpected_error; - atomic_long_t mesq_noop_unexpected_error; - atomic_long_t mesq_noop_lb_overflow; - atomic_long_t mesq_noop_qlimit_reached; - atomic_long_t mesq_noop_amo_nacked; - atomic_long_t mesq_noop_put_nacked; - atomic_long_t mesq_noop_page_overflow; - -}; - -enum mcs_op {cchop_allocate, cchop_start, cchop_interrupt, cchop_interrupt_sync, - cchop_deallocate, tfhop_write_only, tfhop_write_restart, - tghop_invalidate, mcsop_last}; - -struct mcs_op_statistic { - atomic_long_t count; - atomic_long_t total; - unsigned long max; -}; - -extern struct mcs_op_statistic mcs_op_statistics[mcsop_last]; - -#define OPT_DPRINT 1 -#define OPT_STATS 2 - - -#define IRQ_GRU 110 /* Starting IRQ number for interrupts */ - -/* Delay in jiffies between attempts to assign a GRU context */ -#define GRU_ASSIGN_DELAY ((HZ * 20) / 1000) - -/* - * If a process has it's context stolen, min delay in jiffies before trying to - * steal a context from another process. - */ -#define GRU_STEAL_DELAY ((HZ * 200) / 1000) - -#define STAT(id) do { \ - if (gru_options & OPT_STATS) \ - atomic_long_inc(&gru_stats.id); \ - } while (0) - -#ifdef CONFIG_SGI_GRU_DEBUG -#define gru_dbg(dev, fmt, x...) \ - do { \ - if (gru_options & OPT_DPRINT) \ - printk(KERN_DEBUG "GRU:%d %s: " fmt, smp_processor_id(), __func__, x);\ - } while (0) -#else -#define gru_dbg(x...) -#endif - -/*----------------------------------------------------------------------------- - * ASID management - */ -#define MAX_ASID 0xfffff0 -#define MIN_ASID 8 -#define ASID_INC 8 /* number of regions */ - -/* Generate a GRU asid value from a GRU base asid & a virtual address. */ -#define VADDR_HI_BIT 64 -#define GRUREGION(addr) ((addr) >> (VADDR_HI_BIT - 3) & 3) -#define GRUASID(asid, addr) ((asid) + GRUREGION(addr)) - -/*------------------------------------------------------------------------------ - * File & VMS Tables - */ - -struct gru_state; - -/* - * This structure is pointed to from the mmstruct via the notifier pointer. - * There is one of these per address space. - */ -struct gru_mm_tracker { /* pack to reduce size */ - unsigned int mt_asid_gen:24; /* ASID wrap count */ - unsigned int mt_asid:24; /* current base ASID for gru */ - unsigned short mt_ctxbitmap:16;/* bitmap of contexts using - asid */ -} __attribute__ ((packed)); - -struct gru_mm_struct { - struct mmu_notifier ms_notifier; - spinlock_t ms_asid_lock; /* protects ASID assignment */ - atomic_t ms_range_active;/* num range_invals active */ - wait_queue_head_t ms_wait_queue; - DECLARE_BITMAP(ms_asidmap, GRU_MAX_GRUS); - struct gru_mm_tracker ms_asids[GRU_MAX_GRUS]; -}; - -/* - * One of these structures is allocated when a GSEG is mmaped. The - * structure is pointed to by the vma->vm_private_data field in the vma struct. - */ -struct gru_vma_data { - spinlock_t vd_lock; /* Serialize access to vma */ - struct list_head vd_head; /* head of linked list of gts */ - long vd_user_options;/* misc user option flags */ - int vd_cbr_au_count; - int vd_dsr_au_count; - unsigned char vd_tlb_preload_count; -}; - -/* - * One of these is allocated for each thread accessing a mmaped GRU. A linked - * list of these structure is hung off the struct gru_vma_data in the mm_struct. - */ -struct gru_thread_state { - struct list_head ts_next; /* list - head at vma-private */ - struct mutex ts_ctxlock; /* load/unload CTX lock */ - struct mm_struct *ts_mm; /* mm currently mapped to - context */ - struct vm_area_struct *ts_vma; /* vma of GRU context */ - struct gru_state *ts_gru; /* GRU where the context is - loaded */ - struct gru_mm_struct *ts_gms; /* asid & ioproc struct */ - unsigned char ts_tlb_preload_count; /* TLB preload pages */ - unsigned long ts_cbr_map; /* map of allocated CBRs */ - unsigned long ts_dsr_map; /* map of allocated DATA - resources */ - unsigned long ts_steal_jiffies;/* jiffies when context last - stolen */ - long ts_user_options;/* misc user option flags */ - pid_t ts_tgid_owner; /* task that is using the - context - for migration */ - short ts_user_blade_id;/* user selected blade */ - signed char ts_user_chiplet_id;/* user selected chiplet */ - unsigned short ts_sizeavail; /* Pagesizes in use */ - int ts_tsid; /* thread that owns the - structure */ - int ts_tlb_int_select;/* target cpu if interrupts - enabled */ - int ts_ctxnum; /* context number where the - context is loaded */ - refcount_t ts_refcnt; /* reference count GTS */ - unsigned char ts_dsr_au_count;/* Number of DSR resources - required for contest */ - unsigned char ts_cbr_au_count;/* Number of CBR resources - required for contest */ - signed char ts_cch_req_slice;/* CCH packet slice */ - signed char ts_blade; /* If >= 0, migrate context if - ref from different blade */ - signed char ts_force_cch_reload; - signed char ts_cbr_idx[GRU_CBR_AU];/* CBR numbers of each - allocated CB */ - int ts_data_valid; /* Indicates if ts_gdata has - valid data */ - struct gru_gseg_statistics ustats; /* User statistics */ - unsigned long ts_gdata[]; /* save area for GRU data (CB, - DS, CBE) */ -}; - -/* - * Threaded programs actually allocate an array of GSEGs when a context is - * created. Each thread uses a separate GSEG. TSID is the index into the GSEG - * array. - */ -#define TSID(a, v) (((a) - (v)->vm_start) / GRU_GSEG_PAGESIZE) -#define UGRUADDR(gts) ((gts)->ts_vma->vm_start + \ - (gts)->ts_tsid * GRU_GSEG_PAGESIZE) - -#define NULLCTX (-1) /* if context not loaded into GRU */ - -/*----------------------------------------------------------------------------- - * GRU State Tables - */ - -/* - * One of these exists for each GRU chiplet. - */ -struct gru_state { - struct gru_blade_state *gs_blade; /* GRU state for entire - blade */ - unsigned long gs_gru_base_paddr; /* Physical address of - gru segments (64) */ - void *gs_gru_base_vaddr; /* Virtual address of - gru segments (64) */ - unsigned short gs_gid; /* unique GRU number */ - unsigned short gs_blade_id; /* blade of GRU */ - unsigned char gs_chiplet_id; /* blade chiplet of GRU */ - unsigned char gs_tgh_local_shift; /* used to pick TGH for - local flush */ - unsigned char gs_tgh_first_remote; /* starting TGH# for - remote flush */ - spinlock_t gs_asid_lock; /* lock used for - assigning asids */ - spinlock_t gs_lock; /* lock used for - assigning contexts */ - - /* -- the following are protected by the gs_asid_lock spinlock ---- */ - unsigned int gs_asid; /* Next availe ASID */ - unsigned int gs_asid_limit; /* Limit of available - ASIDs */ - unsigned int gs_asid_gen; /* asid generation. - Inc on wrap */ - - /* --- the following fields are protected by the gs_lock spinlock --- */ - unsigned long gs_context_map; /* bitmap to manage - contexts in use */ - unsigned long gs_cbr_map; /* bitmap to manage CB - resources */ - unsigned long gs_dsr_map; /* bitmap used to manage - DATA resources */ - unsigned int gs_reserved_cbrs; /* Number of kernel- - reserved cbrs */ - unsigned int gs_reserved_dsr_bytes; /* Bytes of kernel- - reserved dsrs */ - unsigned short gs_active_contexts; /* number of contexts - in use */ - struct gru_thread_state *gs_gts[GRU_NUM_CCH]; /* GTS currently using - the context */ - int gs_irq[GRU_NUM_TFM]; /* Interrupt irqs */ -}; - -/* - * This structure contains the GRU state for all the GRUs on a blade. - */ -struct gru_blade_state { - void *kernel_cb; /* First kernel - reserved cb */ - void *kernel_dsr; /* First kernel - reserved DSR */ - struct rw_semaphore bs_kgts_sema; /* lock for kgts */ - struct gru_thread_state *bs_kgts; /* GTS for kernel use */ - - /* ---- the following are used for managing kernel async GRU CBRs --- */ - int bs_async_dsr_bytes; /* DSRs for async */ - int bs_async_cbrs; /* CBRs AU for async */ - struct completion *bs_async_wq; - - /* ---- the following are protected by the bs_lock spinlock ---- */ - spinlock_t bs_lock; /* lock used for - stealing contexts */ - int bs_lru_ctxnum; /* STEAL - last context - stolen */ - struct gru_state *bs_lru_gru; /* STEAL - last gru - stolen */ - - struct gru_state bs_grus[GRU_CHIPLETS_PER_BLADE]; -}; - -/*----------------------------------------------------------------------------- - * Address Primitives - */ -#define get_tfm_for_cpu(g, c) \ - ((struct gru_tlb_fault_map *)get_tfm((g)->gs_gru_base_vaddr, (c))) -#define get_tfh_by_index(g, i) \ - ((struct gru_tlb_fault_handle *)get_tfh((g)->gs_gru_base_vaddr, (i))) -#define get_tgh_by_index(g, i) \ - ((struct gru_tlb_global_handle *)get_tgh((g)->gs_gru_base_vaddr, (i))) -#define get_cbe_by_index(g, i) \ - ((struct gru_control_block_extended *)get_cbe((g)->gs_gru_base_vaddr,\ - (i))) - -/*----------------------------------------------------------------------------- - * Useful Macros - */ - -/* Given a blade# & chiplet#, get a pointer to the GRU */ -#define get_gru(b, c) (&gru_base[b]->bs_grus[c]) - -/* Number of bytes to save/restore when unloading/loading GRU contexts */ -#define DSR_BYTES(dsr) ((dsr) * GRU_DSR_AU_BYTES) -#define CBR_BYTES(cbr) ((cbr) * GRU_HANDLE_BYTES * GRU_CBR_AU_SIZE * 2) - -/* Convert a user CB number to the actual CBRNUM */ -#define thread_cbr_number(gts, n) ((gts)->ts_cbr_idx[(n) / GRU_CBR_AU_SIZE] \ - * GRU_CBR_AU_SIZE + (n) % GRU_CBR_AU_SIZE) - -/* Convert a gid to a pointer to the GRU */ -#define GID_TO_GRU(gid) \ - (gru_base[(gid) / GRU_CHIPLETS_PER_BLADE] ? \ - (&gru_base[(gid) / GRU_CHIPLETS_PER_BLADE]-> \ - bs_grus[(gid) % GRU_CHIPLETS_PER_BLADE]) : \ - NULL) - -/* Scan all active GRUs in a GRU bitmap */ -#define for_each_gru_in_bitmap(gid, map) \ - for_each_set_bit((gid), (map), GRU_MAX_GRUS) - -/* Scan all active GRUs on a specific blade */ -#define for_each_gru_on_blade(gru, nid, i) \ - for ((gru) = gru_base[nid]->bs_grus, (i) = 0; \ - (i) < GRU_CHIPLETS_PER_BLADE; \ - (i)++, (gru)++) - -/* Scan all GRUs */ -#define foreach_gid(gid) \ - for ((gid) = 0; (gid) < gru_max_gids; (gid)++) - -/* Scan all active GTSs on a gru. Note: must hold ss_lock to use this macro. */ -#define for_each_gts_on_gru(gts, gru, ctxnum) \ - for ((ctxnum) = 0; (ctxnum) < GRU_NUM_CCH; (ctxnum)++) \ - if (((gts) = (gru)->gs_gts[ctxnum])) - -/* Scan each CBR whose bit is set in a TFM (or copy of) */ -#define for_each_cbr_in_tfm(i, map) \ - for_each_set_bit((i), (map), GRU_NUM_CBE) - -/* Scan each CBR in a CBR bitmap. Note: multiple CBRs in an allocation unit */ -#define for_each_cbr_in_allocation_map(i, map, k) \ - for_each_set_bit((k), (map), GRU_CBR_AU) \ - for ((i) = (k)*GRU_CBR_AU_SIZE; \ - (i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++) - -#define gseg_physical_address(gru, ctxnum) \ - ((gru)->gs_gru_base_paddr + ctxnum * GRU_GSEG_STRIDE) -#define gseg_virtual_address(gru, ctxnum) \ - ((gru)->gs_gru_base_vaddr + ctxnum * GRU_GSEG_STRIDE) - -/*----------------------------------------------------------------------------- - * Lock / Unlock GRU handles - * Use the "delresp" bit in the handle as a "lock" bit. - */ - -/* Lock hierarchy checking enabled only in emulator */ - -/* 0 = lock failed, 1 = locked */ -static inline int __trylock_handle(void *h) -{ - return !test_and_set_bit(1, h); -} - -static inline void __lock_handle(void *h) -{ - while (test_and_set_bit(1, h)) - cpu_relax(); -} - -static inline void __unlock_handle(void *h) -{ - clear_bit(1, h); -} - -static inline int trylock_cch_handle(struct gru_context_configuration_handle *cch) -{ - return __trylock_handle(cch); -} - -static inline void lock_cch_handle(struct gru_context_configuration_handle *cch) -{ - __lock_handle(cch); -} - -static inline void unlock_cch_handle(struct gru_context_configuration_handle - *cch) -{ - __unlock_handle(cch); -} - -static inline void lock_tgh_handle(struct gru_tlb_global_handle *tgh) -{ - __lock_handle(tgh); -} - -static inline void unlock_tgh_handle(struct gru_tlb_global_handle *tgh) -{ - __unlock_handle(tgh); -} - -static inline int is_kernel_context(struct gru_thread_state *gts) -{ - return !gts->ts_mm; -} - -/* - * The following are for Nehelem-EX. A more general scheme is needed for - * future processors. - */ -#define UV_MAX_INT_CORES 8 -#define uv_cpu_socket_number(p) ((cpu_physical_id(p) >> 5) & 1) -#define uv_cpu_ht_number(p) (cpu_physical_id(p) & 1) -#define uv_cpu_core_number(p) (((cpu_physical_id(p) >> 2) & 4) | \ - ((cpu_physical_id(p) >> 1) & 3)) -/*----------------------------------------------------------------------------- - * Function prototypes & externs - */ -struct gru_unload_context_req; - -extern const struct vm_operations_struct gru_vm_ops; -extern struct device *grudev; - -extern struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, - int tsid); -extern struct gru_thread_state *gru_find_thread_state(struct vm_area_struct - *vma, int tsid); -extern struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct - *vma, int tsid); -extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts); -extern void gru_load_context(struct gru_thread_state *gts); -extern void gru_steal_context(struct gru_thread_state *gts); -extern void gru_unload_context(struct gru_thread_state *gts, int savestate); -extern int gru_update_cch(struct gru_thread_state *gts); -extern void gts_drop(struct gru_thread_state *gts); -extern void gru_tgh_flush_init(struct gru_state *gru); -extern int gru_kservices_init(void); -extern void gru_kservices_exit(void); -extern irqreturn_t gru0_intr(int irq, void *dev_id); -extern irqreturn_t gru1_intr(int irq, void *dev_id); -extern irqreturn_t gru_intr_mblade(int irq, void *dev_id); -extern int gru_dump_chiplet_request(unsigned long arg); -extern long gru_get_gseg_statistics(unsigned long arg); -extern int gru_handle_user_call_os(unsigned long address); -extern int gru_user_flush_tlb(unsigned long arg); -extern int gru_user_unload_context(unsigned long arg); -extern int gru_get_exception_detail(unsigned long arg); -extern int gru_set_context_option(unsigned long address); -extern int gru_check_context_placement(struct gru_thread_state *gts); -extern int gru_cpu_fault_map_id(void); -extern struct vm_area_struct *gru_find_vma(unsigned long vaddr); -extern void gru_flush_all_tlb(struct gru_state *gru); -extern int gru_proc_init(void); -extern void gru_proc_exit(void); - -extern struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, - int cbr_au_count, int dsr_au_count, - unsigned char tlb_preload_count, int options, int tsid); -extern unsigned long gru_reserve_cb_resources(struct gru_state *gru, - int cbr_au_count, signed char *cbmap); -extern unsigned long gru_reserve_ds_resources(struct gru_state *gru, - int dsr_au_count, signed char *dsmap); -extern vm_fault_t gru_fault(struct vm_fault *vmf); -extern struct gru_mm_struct *gru_register_mmu_notifier(void); -extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms); - -extern int gru_ktest(unsigned long arg); -extern void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start, - unsigned long len); - -extern unsigned long gru_options; - -#endif /* __GRUTABLES_H__ */ diff --git a/drivers/misc/sgi-gru/grutlbpurge.c b/drivers/misc/sgi-gru/grutlbpurge.c deleted file mode 100644 index a8121d40be68..000000000000 --- a/drivers/misc/sgi-gru/grutlbpurge.c +++ /dev/null @@ -1,316 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * MMUOPS callbacks + TLB flushing - * - * This file handles emu notifier callbacks from the core kernel. The callbacks - * are used to update the TLB in the GRU as a result of changes in the - * state of a process address space. This file also handles TLB invalidates - * from the GRU driver. - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/spinlock.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/device.h> -#include <linux/hugetlb.h> -#include <linux/delay.h> -#include <linux/timex.h> -#include <linux/srcu.h> -#include <asm/processor.h> -#include "gru.h" -#include "grutables.h" -#include <asm/uv/uv_hub.h> - -#define gru_random() get_cycles() - -/* ---------------------------------- TLB Invalidation functions -------- - * get_tgh_handle - * - * Find a TGH to use for issuing a TLB invalidate. For GRUs that are on the - * local blade, use a fixed TGH that is a function of the blade-local cpu - * number. Normally, this TGH is private to the cpu & no contention occurs for - * the TGH. For offblade GRUs, select a random TGH in the range above the - * private TGHs. A spinlock is required to access this TGH & the lock must be - * released when the invalidate is completes. This sucks, but it is the best we - * can do. - * - * Note that the spinlock is IN the TGH handle so locking does not involve - * additional cache lines. - * - */ -static inline int get_off_blade_tgh(struct gru_state *gru) -{ - int n; - - n = GRU_NUM_TGH - gru->gs_tgh_first_remote; - n = gru_random() % n; - n += gru->gs_tgh_first_remote; - return n; -} - -static inline int get_on_blade_tgh(struct gru_state *gru) -{ - return uv_blade_processor_id() >> gru->gs_tgh_local_shift; -} - -static struct gru_tlb_global_handle *get_lock_tgh_handle(struct gru_state - *gru) -{ - struct gru_tlb_global_handle *tgh; - int n; - - if (uv_numa_blade_id() == gru->gs_blade_id) - n = get_on_blade_tgh(gru); - else - n = get_off_blade_tgh(gru); - tgh = get_tgh_by_index(gru, n); - lock_tgh_handle(tgh); - - return tgh; -} - -static void get_unlock_tgh_handle(struct gru_tlb_global_handle *tgh) -{ - unlock_tgh_handle(tgh); -} - -/* - * gru_flush_tlb_range - * - * General purpose TLB invalidation function. This function scans every GRU in - * the ENTIRE system (partition) looking for GRUs where the specified MM has - * been accessed by the GRU. For each GRU found, the TLB must be invalidated OR - * the ASID invalidated. Invalidating an ASID causes a new ASID to be assigned - * on the next fault. This effectively flushes the ENTIRE TLB for the MM at the - * cost of (possibly) a large number of future TLBmisses. - * - * The current algorithm is optimized based on the following (somewhat true) - * assumptions: - * - GRU contexts are not loaded into a GRU unless a reference is made to - * the data segment or control block (this is true, not an assumption). - * If a DS/CB is referenced, the user will also issue instructions that - * cause TLBmisses. It is not necessary to optimize for the case where - * contexts are loaded but no instructions cause TLB misses. (I know - * this will happen but I'm not optimizing for it). - * - GRU instructions to invalidate TLB entries are SLOOOOWWW - normally - * a few usec but in unusual cases, it could be longer. Avoid if - * possible. - * - intrablade process migration between cpus is not frequent but is - * common. - * - a GRU context is not typically migrated to a different GRU on the - * blade because of intrablade migration - * - interblade migration is rare. Processes migrate their GRU context to - * the new blade. - * - if interblade migration occurs, migration back to the original blade - * is very very rare (ie., no optimization for this case) - * - most GRU instruction operate on a subset of the user REGIONS. Code - * & shared library regions are not likely targets of GRU instructions. - * - * To help improve the efficiency of TLB invalidation, the GMS data - * structure is maintained for EACH address space (MM struct). The GMS is - * also the structure that contains the pointer to the mmu callout - * functions. This structure is linked to the mm_struct for the address space - * using the mmu "register" function. The mmu interfaces are used to - * provide the callbacks for TLB invalidation. The GMS contains: - * - * - asid[maxgrus] array. ASIDs are assigned to a GRU when a context is - * loaded into the GRU. - * - asidmap[maxgrus]. bitmap to make it easier to find non-zero asids in - * the above array - * - ctxbitmap[maxgrus]. Indicates the contexts that are currently active - * in the GRU for the address space. This bitmap must be passed to the - * GRU to do an invalidate. - * - * The current algorithm for invalidating TLBs is: - * - scan the asidmap for GRUs where the context has been loaded, ie, - * asid is non-zero. - * - for each gru found: - * - if the ctxtmap is non-zero, there are active contexts in the - * GRU. TLB invalidate instructions must be issued to the GRU. - * - if the ctxtmap is zero, no context is active. Set the ASID to - * zero to force a full TLB invalidation. This is fast but will - * cause a lot of TLB misses if the context is reloaded onto the - * GRU - * - */ - -void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start, - unsigned long len) -{ - struct gru_state *gru; - struct gru_mm_tracker *asids; - struct gru_tlb_global_handle *tgh; - unsigned long num; - int grupagesize, pagesize, pageshift, gid, asid; - - /* ZZZ TODO - handle huge pages */ - pageshift = PAGE_SHIFT; - pagesize = (1UL << pageshift); - grupagesize = GRU_PAGESIZE(pageshift); - num = min(((len + pagesize - 1) >> pageshift), GRUMAXINVAL); - - STAT(flush_tlb); - gru_dbg(grudev, "gms %p, start 0x%lx, len 0x%lx, asidmap 0x%lx\n", gms, - start, len, gms->ms_asidmap[0]); - - spin_lock(&gms->ms_asid_lock); - for_each_gru_in_bitmap(gid, gms->ms_asidmap) { - STAT(flush_tlb_gru); - gru = GID_TO_GRU(gid); - asids = gms->ms_asids + gid; - asid = asids->mt_asid; - if (asids->mt_ctxbitmap && asid) { - STAT(flush_tlb_gru_tgh); - asid = GRUASID(asid, start); - gru_dbg(grudev, - " FLUSH gruid %d, asid 0x%x, vaddr 0x%lx, vamask 0x%x, num %ld, cbmap 0x%x\n", - gid, asid, start, grupagesize, num, asids->mt_ctxbitmap); - tgh = get_lock_tgh_handle(gru); - tgh_invalidate(tgh, start, ~0, asid, grupagesize, 0, - num - 1, asids->mt_ctxbitmap); - get_unlock_tgh_handle(tgh); - } else { - STAT(flush_tlb_gru_zero_asid); - asids->mt_asid = 0; - __clear_bit(gru->gs_gid, gms->ms_asidmap); - gru_dbg(grudev, - " CLEARASID gruid %d, asid 0x%x, cbtmap 0x%x, asidmap 0x%lx\n", - gid, asid, asids->mt_ctxbitmap, - gms->ms_asidmap[0]); - } - } - spin_unlock(&gms->ms_asid_lock); -} - -/* - * Flush the entire TLB on a chiplet. - */ -void gru_flush_all_tlb(struct gru_state *gru) -{ - struct gru_tlb_global_handle *tgh; - - gru_dbg(grudev, "gid %d\n", gru->gs_gid); - tgh = get_lock_tgh_handle(gru); - tgh_invalidate(tgh, 0, ~0, 0, 1, 1, GRUMAXINVAL - 1, 0xffff); - get_unlock_tgh_handle(tgh); -} - -/* - * MMUOPS notifier callout functions - */ -static int gru_invalidate_range_start(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct, - ms_notifier); - - STAT(mmu_invalidate_range); - atomic_inc(&gms->ms_range_active); - gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx, act %d\n", gms, - range->start, range->end, atomic_read(&gms->ms_range_active)); - gru_flush_tlb_range(gms, range->start, range->end - range->start); - - return 0; -} - -static void gru_invalidate_range_end(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct, - ms_notifier); - - /* ..._and_test() provides needed barrier */ - (void)atomic_dec_and_test(&gms->ms_range_active); - - wake_up_all(&gms->ms_wait_queue); - gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx\n", - gms, range->start, range->end); -} - -static struct mmu_notifier *gru_alloc_notifier(struct mm_struct *mm) -{ - struct gru_mm_struct *gms; - - gms = kzalloc_obj(*gms); - if (!gms) - return ERR_PTR(-ENOMEM); - STAT(gms_alloc); - spin_lock_init(&gms->ms_asid_lock); - init_waitqueue_head(&gms->ms_wait_queue); - - return &gms->ms_notifier; -} - -static void gru_free_notifier(struct mmu_notifier *mn) -{ - kfree(container_of(mn, struct gru_mm_struct, ms_notifier)); - STAT(gms_free); -} - -static const struct mmu_notifier_ops gru_mmuops = { - .invalidate_range_start = gru_invalidate_range_start, - .invalidate_range_end = gru_invalidate_range_end, - .alloc_notifier = gru_alloc_notifier, - .free_notifier = gru_free_notifier, -}; - -struct gru_mm_struct *gru_register_mmu_notifier(void) -{ - struct mmu_notifier *mn; - - mn = mmu_notifier_get_locked(&gru_mmuops, current->mm); - if (IS_ERR(mn)) - return ERR_CAST(mn); - - return container_of(mn, struct gru_mm_struct, ms_notifier); -} - -void gru_drop_mmu_notifier(struct gru_mm_struct *gms) -{ - mmu_notifier_put(&gms->ms_notifier); -} - -/* - * Setup TGH parameters. There are: - * - 24 TGH handles per GRU chiplet - * - a portion (MAX_LOCAL_TGH) of the handles are reserved for - * use by blade-local cpus - * - the rest are used by off-blade cpus. This usage is - * less frequent than blade-local usage. - * - * For now, use 16 handles for local flushes, 8 for remote flushes. If the blade - * has less tan or equal to 16 cpus, each cpu has a unique handle that it can - * use. - */ -#define MAX_LOCAL_TGH 16 - -void gru_tgh_flush_init(struct gru_state *gru) -{ - int cpus, shift = 0, n; - - cpus = uv_blade_nr_possible_cpus(gru->gs_blade_id); - - /* n = cpus rounded up to next power of 2 */ - if (cpus) { - n = 1 << fls(cpus - 1); - - /* - * shift count for converting local cpu# to TGH index - * 0 if cpus <= MAX_LOCAL_TGH, - * 1 if cpus <= 2*MAX_LOCAL_TGH, - * etc - */ - shift = max(0, fls(n - 1) - fls(MAX_LOCAL_TGH - 1)); - } - gru->gs_tgh_local_shift = shift; - - /* first starting TGH index to use for remote purges */ - gru->gs_tgh_first_remote = (cpus + (1 << shift) - 1) >> shift; - -} diff --git a/drivers/misc/sgi-xp/Makefile b/drivers/misc/sgi-xp/Makefile deleted file mode 100644 index 34c55a4045af..000000000000 --- a/drivers/misc/sgi-xp/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for SGI's XP devices. -# - -obj-$(CONFIG_SGI_XP) += xp.o -xp-y := xp_main.o xp_uv.o - -obj-$(CONFIG_SGI_XP) += xpc.o -xpc-y := xpc_main.o xpc_channel.o xpc_partition.o \ - xpc_uv.o - -obj-$(CONFIG_SGI_XP) += xpnet.o diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h deleted file mode 100644 index 3185711beb07..000000000000 --- a/drivers/misc/sgi-xp/xp.h +++ /dev/null @@ -1,341 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (C) 2004-2008 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * External Cross Partition (XP) structures and defines. - */ - -#ifndef _DRIVERS_MISC_SGIXP_XP_H -#define _DRIVERS_MISC_SGIXP_XP_H - -#include <linux/mutex.h> - -#if defined CONFIG_X86_UV -#include <asm/uv/uv.h> -#endif - -#ifdef USE_DBUG_ON -#define DBUG_ON(condition) BUG_ON(condition) -#else -#define DBUG_ON(condition) -#endif - -/* - * Define the maximum number of partitions the system can possibly support. - * It is based on the maximum number of hardware partitionable regions. The - * term 'region' in this context refers to the minimum number of nodes that - * can comprise an access protection grouping. The access protection is in - * regards to memory, IPI and IOI. - * - * The maximum number of hardware partitionable regions is equal to the - * maximum number of nodes in the entire system divided by the minimum number - * of nodes that comprise an access protection grouping. - */ -#define XP_MAX_NPARTITIONS_SN2 64 -#define XP_MAX_NPARTITIONS_UV 256 - -/* - * XPC establishes channel connections between the local partition and any - * other partition that is currently up. Over these channels, kernel-level - * `users' can communicate with their counterparts on the other partitions. - * - * If the need for additional channels arises, one can simply increase - * XPC_MAX_NCHANNELS accordingly. If the day should come where that number - * exceeds the absolute MAXIMUM number of channels possible (eight), then one - * will need to make changes to the XPC code to accommodate for this. - * - * The absolute maximum number of channels possible is limited to eight for - * performance reasons on sn2 hardware. The internal cross partition structures - * require sixteen bytes per channel, and eight allows all of this - * interface-shared info to fit in one 128-byte cacheline. - */ -#define XPC_MEM_CHANNEL 0 /* memory channel number */ -#define XPC_NET_CHANNEL 1 /* network channel number */ - -#define XPC_MAX_NCHANNELS 2 /* max #of channels allowed */ - -#if XPC_MAX_NCHANNELS > 8 -#error XPC_MAX_NCHANNELS exceeds absolute MAXIMUM possible. -#endif - -/* - * Define macro, XPC_MSG_SIZE(), is provided for the user - * that wants to fit as many msg entries as possible in a given memory size - * (e.g. a memory page). - */ -#define XPC_MSG_MAX_SIZE 128 -#define XPC_MSG_HDR_MAX_SIZE 16 -#define XPC_MSG_PAYLOAD_MAX_SIZE (XPC_MSG_MAX_SIZE - XPC_MSG_HDR_MAX_SIZE) - -#define XPC_MSG_SIZE(_payload_size) \ - ALIGN(XPC_MSG_HDR_MAX_SIZE + (_payload_size), \ - is_uv_system() ? 64 : 128) - - -/* - * Define the return values and values passed to user's callout functions. - * (It is important to add new value codes at the end just preceding - * xpUnknownReason, which must have the highest numerical value.) - */ -enum xp_retval { - xpSuccess = 0, - - xpNotConnected, /* 1: channel is not connected */ - xpConnected, /* 2: channel connected (opened) */ - xpRETIRED1, /* 3: (formerly xpDisconnected) */ - - xpMsgReceived, /* 4: message received */ - xpMsgDelivered, /* 5: message delivered and acknowledged */ - - xpRETIRED2, /* 6: (formerly xpTransferFailed) */ - - xpNoWait, /* 7: operation would require wait */ - xpRetry, /* 8: retry operation */ - xpTimeout, /* 9: timeout in xpc_allocate_msg_wait() */ - xpInterrupted, /* 10: interrupted wait */ - - xpUnequalMsgSizes, /* 11: message size disparity between sides */ - xpInvalidAddress, /* 12: invalid address */ - - xpNoMemory, /* 13: no memory available for XPC structures */ - xpLackOfResources, /* 14: insufficient resources for operation */ - xpUnregistered, /* 15: channel is not registered */ - xpAlreadyRegistered, /* 16: channel is already registered */ - - xpPartitionDown, /* 17: remote partition is down */ - xpNotLoaded, /* 18: XPC module is not loaded */ - xpUnloading, /* 19: this side is unloading XPC module */ - - xpBadMagic, /* 20: XPC MAGIC string not found */ - - xpReactivating, /* 21: remote partition was reactivated */ - - xpUnregistering, /* 22: this side is unregistering channel */ - xpOtherUnregistering, /* 23: other side is unregistering channel */ - - xpCloneKThread, /* 24: cloning kernel thread */ - xpCloneKThreadFailed, /* 25: cloning kernel thread failed */ - - xpNoHeartbeat, /* 26: remote partition has no heartbeat */ - - xpPioReadError, /* 27: PIO read error */ - xpPhysAddrRegFailed, /* 28: registration of phys addr range failed */ - - xpRETIRED3, /* 29: (formerly xpBteDirectoryError) */ - xpRETIRED4, /* 30: (formerly xpBtePoisonError) */ - xpRETIRED5, /* 31: (formerly xpBteWriteError) */ - xpRETIRED6, /* 32: (formerly xpBteAccessError) */ - xpRETIRED7, /* 33: (formerly xpBtePWriteError) */ - xpRETIRED8, /* 34: (formerly xpBtePReadError) */ - xpRETIRED9, /* 35: (formerly xpBteTimeOutError) */ - xpRETIRED10, /* 36: (formerly xpBteXtalkError) */ - xpRETIRED11, /* 37: (formerly xpBteNotAvailable) */ - xpRETIRED12, /* 38: (formerly xpBteUnmappedError) */ - - xpBadVersion, /* 39: bad version number */ - xpVarsNotSet, /* 40: the XPC variables are not set up */ - xpNoRsvdPageAddr, /* 41: unable to get rsvd page's phys addr */ - xpInvalidPartid, /* 42: invalid partition ID */ - xpLocalPartid, /* 43: local partition ID */ - - xpOtherGoingDown, /* 44: other side going down, reason unknown */ - xpSystemGoingDown, /* 45: system is going down, reason unknown */ - xpSystemHalt, /* 46: system is being halted */ - xpSystemReboot, /* 47: system is being rebooted */ - xpSystemPoweroff, /* 48: system is being powered off */ - - xpDisconnecting, /* 49: channel disconnecting (closing) */ - - xpOpenCloseError, /* 50: channel open/close protocol error */ - - xpDisconnected, /* 51: channel disconnected (closed) */ - - xpBteCopyError, /* 52: bte_copy() returned error */ - xpSalError, /* 53: sn SAL error */ - xpRsvdPageNotSet, /* 54: the reserved page is not set up */ - xpPayloadTooBig, /* 55: payload too large for message slot */ - - xpUnsupported, /* 56: unsupported functionality or resource */ - xpNeedMoreInfo, /* 57: more info is needed by SAL */ - - xpGruCopyError, /* 58: gru_copy_gru() returned error */ - xpGruSendMqError, /* 59: gru send message queue related error */ - - xpBadChannelNumber, /* 60: invalid channel number */ - xpBadMsgType, /* 61: invalid message type */ - xpBiosError, /* 62: BIOS error */ - - xpUnknownReason /* 63: unknown reason - must be last in enum */ -}; - -/* - * Define the callout function type used by XPC to update the user on - * connection activity and state changes via the user function registered - * by xpc_connect(). - * - * Arguments: - * - * reason - reason code. - * partid - partition ID associated with condition. - * ch_number - channel # associated with condition. - * data - pointer to optional data. - * key - pointer to optional user-defined value provided as the "key" - * argument to xpc_connect(). - * - * A reason code of xpConnected indicates that a connection has been - * established to the specified partition on the specified channel. The data - * argument indicates the max number of entries allowed in the message queue. - * - * A reason code of xpMsgReceived indicates that a XPC message arrived from - * the specified partition on the specified channel. The data argument - * specifies the address of the message's payload. The user must call - * xpc_received() when finished with the payload. - * - * All other reason codes indicate failure. The data argmument is NULL. - * When a failure reason code is received, one can assume that the channel - * is not connected. - */ -typedef void (*xpc_channel_func) (enum xp_retval reason, short partid, - int ch_number, void *data, void *key); - -/* - * Define the callout function type used by XPC to notify the user of - * messages received and delivered via the user function registered by - * xpc_send_notify(). - * - * Arguments: - * - * reason - reason code. - * partid - partition ID associated with condition. - * ch_number - channel # associated with condition. - * key - pointer to optional user-defined value provided as the "key" - * argument to xpc_send_notify(). - * - * A reason code of xpMsgDelivered indicates that the message was delivered - * to the intended recipient and that they have acknowledged its receipt by - * calling xpc_received(). - * - * All other reason codes indicate failure. - * - * NOTE: The user defined function must be callable by an interrupt handler - * and thus cannot block. - */ -typedef void (*xpc_notify_func) (enum xp_retval reason, short partid, - int ch_number, void *key); - -/* - * The following is a registration entry. There is a global array of these, - * one per channel. It is used to record the connection registration made - * by the users of XPC. As long as a registration entry exists, for any - * partition that comes up, XPC will attempt to establish a connection on - * that channel. Notification that a connection has been made will occur via - * the xpc_channel_func function. - * - * The 'func' field points to the function to call when aynchronous - * notification is required for such events as: a connection established/lost, - * or an incoming message received, or an error condition encountered. A - * non-NULL 'func' field indicates that there is an active registration for - * the channel. - */ -struct xpc_registration { - struct mutex mutex; - xpc_channel_func func; /* function to call */ - void *key; /* pointer to user's key */ - u16 nentries; /* #of msg entries in local msg queue */ - u16 entry_size; /* message queue's message entry size */ - u32 assigned_limit; /* limit on #of assigned kthreads */ - u32 idle_limit; /* limit on #of idle kthreads */ -} ____cacheline_aligned; - -#define XPC_CHANNEL_REGISTERED(_c) (xpc_registrations[_c].func != NULL) - -/* the following are valid xpc_send() or xpc_send_notify() flags */ -#define XPC_WAIT 0 /* wait flag */ -#define XPC_NOWAIT 1 /* no wait flag */ - -struct xpc_interface { - void (*connect) (int); - void (*disconnect) (int); - enum xp_retval (*send) (short, int, u32, void *, u16); - enum xp_retval (*send_notify) (short, int, u32, void *, u16, - xpc_notify_func, void *); - void (*received) (short, int, void *); - enum xp_retval (*partid_to_nasids) (short, void *); -}; - -extern struct xpc_interface xpc_interface; - -extern void xpc_set_interface(void (*)(int), - void (*)(int), - enum xp_retval (*)(short, int, u32, void *, u16), - enum xp_retval (*)(short, int, u32, void *, u16, - xpc_notify_func, void *), - void (*)(short, int, void *), - enum xp_retval (*)(short, void *)); -extern void xpc_clear_interface(void); - -extern enum xp_retval xpc_connect(int, xpc_channel_func, void *, u16, - u16, u32, u32); -extern void xpc_disconnect(int); - -static inline enum xp_retval -xpc_send(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size) -{ - if (!xpc_interface.send) - return xpNotLoaded; - - return xpc_interface.send(partid, ch_number, flags, payload, - payload_size); -} - -static inline enum xp_retval -xpc_send_notify(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size, xpc_notify_func func, void *key) -{ - if (!xpc_interface.send_notify) - return xpNotLoaded; - - return xpc_interface.send_notify(partid, ch_number, flags, payload, - payload_size, func, key); -} - -static inline void -xpc_received(short partid, int ch_number, void *payload) -{ - if (xpc_interface.received) - xpc_interface.received(partid, ch_number, payload); -} - -static inline enum xp_retval -xpc_partid_to_nasids(short partid, void *nasids) -{ - if (!xpc_interface.partid_to_nasids) - return xpNotLoaded; - - return xpc_interface.partid_to_nasids(partid, nasids); -} - -extern short xp_max_npartitions; -extern short xp_partition_id; -extern u8 xp_region_size; - -extern unsigned long (*xp_pa) (void *); -extern unsigned long (*xp_socket_pa) (unsigned long); -extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long, - size_t); -extern int (*xp_cpu_to_nasid) (int); -extern enum xp_retval (*xp_expand_memprotect) (unsigned long, unsigned long); -extern enum xp_retval (*xp_restrict_memprotect) (unsigned long, unsigned long); - -extern struct device *xp; -extern enum xp_retval xp_init_uv(void); -extern void xp_exit_uv(void); - -#endif /* _DRIVERS_MISC_SGIXP_XP_H */ diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c deleted file mode 100644 index 87d156c15f35..000000000000 --- a/drivers/misc/sgi-xp/xp_main.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition (XP) base. - * - * XP provides a base from which its users can interact - * with XPC, yet not be dependent on XPC. - * - */ - -#include <linux/module.h> -#include <linux/device.h> -#include "xp.h" - -/* define the XP debug device structures to be used with dev_dbg() et al */ - -static struct device_driver xp_dbg_name = { - .name = "xp" -}; - -static struct device xp_dbg_subname = { - .init_name = "", /* set to "" */ - .driver = &xp_dbg_name -}; - -struct device *xp = &xp_dbg_subname; - -/* max #of partitions possible */ -short xp_max_npartitions; -EXPORT_SYMBOL_GPL(xp_max_npartitions); - -short xp_partition_id; -EXPORT_SYMBOL_GPL(xp_partition_id); - -u8 xp_region_size; -EXPORT_SYMBOL_GPL(xp_region_size); - -unsigned long (*xp_pa) (void *addr); -EXPORT_SYMBOL_GPL(xp_pa); - -unsigned long (*xp_socket_pa) (unsigned long gpa); -EXPORT_SYMBOL_GPL(xp_socket_pa); - -enum xp_retval (*xp_remote_memcpy) (unsigned long dst_gpa, - const unsigned long src_gpa, size_t len); -EXPORT_SYMBOL_GPL(xp_remote_memcpy); - -int (*xp_cpu_to_nasid) (int cpuid); -EXPORT_SYMBOL_GPL(xp_cpu_to_nasid); - -enum xp_retval (*xp_expand_memprotect) (unsigned long phys_addr, - unsigned long size); -EXPORT_SYMBOL_GPL(xp_expand_memprotect); -enum xp_retval (*xp_restrict_memprotect) (unsigned long phys_addr, - unsigned long size); -EXPORT_SYMBOL_GPL(xp_restrict_memprotect); - -/* - * xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level - * users of XPC. - */ -struct xpc_registration xpc_registrations[XPC_MAX_NCHANNELS]; -EXPORT_SYMBOL_GPL(xpc_registrations); - -/* - * Initialize the XPC interface to NULL to indicate that XPC isn't loaded. - */ -struct xpc_interface xpc_interface = { }; -EXPORT_SYMBOL_GPL(xpc_interface); - -/* - * XPC calls this when it (the XPC module) has been loaded. - */ -void -xpc_set_interface(void (*connect) (int), - void (*disconnect) (int), - enum xp_retval (*send) (short, int, u32, void *, u16), - enum xp_retval (*send_notify) (short, int, u32, void *, u16, - xpc_notify_func, void *), - void (*received) (short, int, void *), - enum xp_retval (*partid_to_nasids) (short, void *)) -{ - xpc_interface.connect = connect; - xpc_interface.disconnect = disconnect; - xpc_interface.send = send; - xpc_interface.send_notify = send_notify; - xpc_interface.received = received; - xpc_interface.partid_to_nasids = partid_to_nasids; -} -EXPORT_SYMBOL_GPL(xpc_set_interface); - -/* - * XPC calls this when it (the XPC module) is being unloaded. - */ -void -xpc_clear_interface(void) -{ - memset(&xpc_interface, 0, sizeof(xpc_interface)); -} -EXPORT_SYMBOL_GPL(xpc_clear_interface); - -/* - * Register for automatic establishment of a channel connection whenever - * a partition comes up. - * - * Arguments: - * - * ch_number - channel # to register for connection. - * func - function to call for asynchronous notification of channel - * state changes (i.e., connection, disconnection, error) and - * the arrival of incoming messages. - * key - pointer to optional user-defined value that gets passed back - * to the user on any callouts made to func. - * payload_size - size in bytes of the XPC message's payload area which - * contains a user-defined message. The user should make - * this large enough to hold their largest message. - * nentries - max #of XPC message entries a message queue can contain. - * The actual number, which is determined when a connection - * is established and may be less then requested, will be - * passed to the user via the xpConnected callout. - * assigned_limit - max number of kthreads allowed to be processing - * messages (per connection) at any given instant. - * idle_limit - max number of kthreads allowed to be idle at any given - * instant. - */ -enum xp_retval -xpc_connect(int ch_number, xpc_channel_func func, void *key, u16 payload_size, - u16 nentries, u32 assigned_limit, u32 idle_limit) -{ - struct xpc_registration *registration; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - DBUG_ON(payload_size == 0 || nentries == 0); - DBUG_ON(func == NULL); - DBUG_ON(assigned_limit == 0 || idle_limit > assigned_limit); - - if (XPC_MSG_SIZE(payload_size) > XPC_MSG_MAX_SIZE) - return xpPayloadTooBig; - - registration = &xpc_registrations[ch_number]; - - if (mutex_lock_interruptible(®istration->mutex) != 0) - return xpInterrupted; - - /* if XPC_CHANNEL_REGISTERED(ch_number) */ - if (registration->func != NULL) { - mutex_unlock(®istration->mutex); - return xpAlreadyRegistered; - } - - /* register the channel for connection */ - registration->entry_size = XPC_MSG_SIZE(payload_size); - registration->nentries = nentries; - registration->assigned_limit = assigned_limit; - registration->idle_limit = idle_limit; - registration->key = key; - registration->func = func; - - mutex_unlock(®istration->mutex); - - if (xpc_interface.connect) - xpc_interface.connect(ch_number); - - return xpSuccess; -} -EXPORT_SYMBOL_GPL(xpc_connect); - -/* - * Remove the registration for automatic connection of the specified channel - * when a partition comes up. - * - * Before returning this xpc_disconnect() will wait for all connections on the - * specified channel have been closed/torndown. So the caller can be assured - * that they will not be receiving any more callouts from XPC to their - * function registered via xpc_connect(). - * - * Arguments: - * - * ch_number - channel # to unregister. - */ -void -xpc_disconnect(int ch_number) -{ - struct xpc_registration *registration; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - - registration = &xpc_registrations[ch_number]; - - /* - * We've decided not to make this a down_interruptible(), since we - * figured XPC's users will just turn around and call xpc_disconnect() - * again anyways, so we might as well wait, if need be. - */ - mutex_lock(®istration->mutex); - - /* if !XPC_CHANNEL_REGISTERED(ch_number) */ - if (registration->func == NULL) { - mutex_unlock(®istration->mutex); - return; - } - - /* remove the connection registration for the specified channel */ - registration->func = NULL; - registration->key = NULL; - registration->nentries = 0; - registration->entry_size = 0; - registration->assigned_limit = 0; - registration->idle_limit = 0; - - if (xpc_interface.disconnect) - xpc_interface.disconnect(ch_number); - - mutex_unlock(®istration->mutex); - - return; -} -EXPORT_SYMBOL_GPL(xpc_disconnect); - -static int __init -xp_init(void) -{ - enum xp_retval ret; - int ch_number; - - /* initialize the connection registration mutex */ - for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) - mutex_init(&xpc_registrations[ch_number].mutex); - - if (is_uv_system()) - ret = xp_init_uv(); - else - ret = 0; - - if (ret != xpSuccess) - return ret; - - return 0; -} - -module_init(xp_init); - -static void __exit -xp_exit(void) -{ - if (is_uv_system()) - xp_exit_uv(); -} - -module_exit(xp_exit); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_DESCRIPTION("Cross Partition (XP) base"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c deleted file mode 100644 index 3faa7eadf679..000000000000 --- a/drivers/misc/sgi-xp/xp_uv.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition (XP) uv-based functions. - * - * Architecture specific implementation of common functions. - * - */ - -#include <linux/device.h> -#include <asm/uv/uv_hub.h> -#if defined CONFIG_X86_64 -#include <asm/uv/bios.h> -#endif -#include "../sgi-gru/grukservices.h" -#include "xp.h" - -/* - * Convert a virtual memory address to a physical memory address. - */ -static unsigned long -xp_pa_uv(void *addr) -{ - return uv_gpa(addr); -} - -/* - * Convert a global physical to socket physical address. - */ -static unsigned long -xp_socket_pa_uv(unsigned long gpa) -{ - return uv_gpa_to_soc_phys_ram(gpa); -} - -static enum xp_retval -xp_remote_mmr_read(unsigned long dst_gpa, const unsigned long src_gpa, - size_t len) -{ - int ret; - unsigned long *dst_va = __va(uv_gpa_to_soc_phys_ram(dst_gpa)); - - BUG_ON(!uv_gpa_in_mmr_space(src_gpa)); - BUG_ON(len != 8); - - ret = gru_read_gpa(dst_va, src_gpa); - if (ret == 0) - return xpSuccess; - - dev_err(xp, "gru_read_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx " - "len=%ld\n", dst_gpa, src_gpa, len); - return xpGruCopyError; -} - - -static enum xp_retval -xp_remote_memcpy_uv(unsigned long dst_gpa, const unsigned long src_gpa, - size_t len) -{ - int ret; - - if (uv_gpa_in_mmr_space(src_gpa)) - return xp_remote_mmr_read(dst_gpa, src_gpa, len); - - ret = gru_copy_gpa(dst_gpa, src_gpa, len); - if (ret == 0) - return xpSuccess; - - dev_err(xp, "gru_copy_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx " - "len=%ld\n", dst_gpa, src_gpa, len); - return xpGruCopyError; -} - -static int -xp_cpu_to_nasid_uv(int cpuid) -{ - /* ??? Is this same as sn2 nasid in mach/part bitmaps set up by SAL? */ - return UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpuid)); -} - -static enum xp_retval -xp_expand_memprotect_uv(unsigned long phys_addr, unsigned long size) -{ - int ret; - -#if defined CONFIG_X86_64 - ret = uv_bios_change_memprotect(phys_addr, size, UV_MEMPROT_ALLOW_RW); - if (ret != BIOS_STATUS_SUCCESS) { - dev_err(xp, "uv_bios_change_memprotect(,, " - "UV_MEMPROT_ALLOW_RW) failed, ret=%d\n", ret); - return xpBiosError; - } -#else - #error not a supported configuration -#endif - return xpSuccess; -} - -static enum xp_retval -xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size) -{ - int ret; - -#if defined CONFIG_X86_64 - ret = uv_bios_change_memprotect(phys_addr, size, - UV_MEMPROT_RESTRICT_ACCESS); - if (ret != BIOS_STATUS_SUCCESS) { - dev_err(xp, "uv_bios_change_memprotect(,, " - "UV_MEMPROT_RESTRICT_ACCESS) failed, ret=%d\n", ret); - return xpBiosError; - } -#else - #error not a supported configuration -#endif - return xpSuccess; -} - -enum xp_retval -xp_init_uv(void) -{ - WARN_ON(!is_uv_system()); - if (!is_uv_system()) - return xpUnsupported; - - xp_max_npartitions = XP_MAX_NPARTITIONS_UV; -#ifdef CONFIG_X86 - xp_partition_id = sn_partition_id; - xp_region_size = sn_region_size; -#endif - xp_pa = xp_pa_uv; - xp_socket_pa = xp_socket_pa_uv; - xp_remote_memcpy = xp_remote_memcpy_uv; - xp_cpu_to_nasid = xp_cpu_to_nasid_uv; - xp_expand_memprotect = xp_expand_memprotect_uv; - xp_restrict_memprotect = xp_restrict_memprotect_uv; - - return xpSuccess; -} - -void -xp_exit_uv(void) -{ - WARN_ON(!is_uv_system()); -} diff --git a/drivers/misc/sgi-xp/xpc.h b/drivers/misc/sgi-xp/xpc.h deleted file mode 100644 index 225f2bb84e39..000000000000 --- a/drivers/misc/sgi-xp/xpc.h +++ /dev/null @@ -1,732 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) structures and macros. - */ - -#ifndef _DRIVERS_MISC_SGIXP_XPC_H -#define _DRIVERS_MISC_SGIXP_XPC_H - -#include <linux/wait.h> -#include <linux/completion.h> -#include <linux/timer.h> -#include <linux/sched.h> -#include "xp.h" - -/* - * XPC Version numbers consist of a major and minor number. XPC can always - * talk to versions with same major #, and never talk to versions with a - * different major #. - */ -#define _XPC_VERSION(_maj, _min) (((_maj) << 4) | ((_min) & 0xf)) -#define XPC_VERSION_MAJOR(_v) ((_v) >> 4) -#define XPC_VERSION_MINOR(_v) ((_v) & 0xf) - -/* define frequency of the heartbeat and frequency how often it's checked */ -#define XPC_HB_DEFAULT_INTERVAL 5 /* incr HB every x secs */ -#define XPC_HB_CHECK_DEFAULT_INTERVAL 20 /* check HB every x secs */ - -/* define the process name of HB checker and the CPU it is pinned to */ -#define XPC_HB_CHECK_THREAD_NAME "xpc_hb" -#define XPC_HB_CHECK_CPU 0 - -/* define the process name of the discovery thread */ -#define XPC_DISCOVERY_THREAD_NAME "xpc_discovery" - -/* - * the reserved page - * - * SAL reserves one page of memory per partition for XPC. Though a full page - * in length (16384 bytes), its starting address is not page aligned, but it - * is cacheline aligned. The reserved page consists of the following: - * - * reserved page header - * - * The first two 64-byte cachelines of the reserved page contain the - * header (struct xpc_rsvd_page). Before SAL initialization has completed, - * SAL has set up the following fields of the reserved page header: - * SAL_signature, SAL_version, SAL_partid, and SAL_nasids_size. The - * other fields are set up by XPC. (xpc_rsvd_page points to the local - * partition's reserved page.) - * - * part_nasids mask - * mach_nasids mask - * - * SAL also sets up two bitmaps (or masks), one that reflects the actual - * nasids in this partition (part_nasids), and the other that reflects - * the actual nasids in the entire machine (mach_nasids). We're only - * interested in the even numbered nasids (which contain the processors - * and/or memory), so we only need half as many bits to represent the - * nasids. When mapping nasid to bit in a mask (or bit to nasid) be sure - * to either divide or multiply by 2. The part_nasids mask is located - * starting at the first cacheline following the reserved page header. The - * mach_nasids mask follows right after the part_nasids mask. The size in - * bytes of each mask is reflected by the reserved page header field - * 'SAL_nasids_size'. (Local partition's mask pointers are xpc_part_nasids - * and xpc_mach_nasids.) - * - * Immediately following the mach_nasids mask are the XPC variables - * required by other partitions. First are those that are generic to all - * partitions (vars), followed on the next available cacheline by those - * which are partition specific (vars part). These are setup by XPC. - * - * Note: Until 'ts_jiffies' is set non-zero, the partition XPC code has not been - * initialized. - */ -struct xpc_rsvd_page { - u64 SAL_signature; /* SAL: unique signature */ - u64 SAL_version; /* SAL: version */ - short SAL_partid; /* SAL: partition ID */ - short max_npartitions; /* value of XPC_MAX_PARTITIONS */ - u8 version; - u8 pad1[3]; /* align to next u64 in 1st 64-byte cacheline */ - unsigned long ts_jiffies; /* timestamp when rsvd pg was setup by XPC */ - union { - struct { - unsigned long heartbeat_gpa; /* phys addr */ - unsigned long activate_gru_mq_desc_gpa; /* phys addr */ - } uv; - } sn; - u64 pad2[9]; /* align to last u64 in 2nd 64-byte cacheline */ - u64 SAL_nasids_size; /* SAL: size of each nasid mask in bytes */ -}; - -#define XPC_RP_VERSION _XPC_VERSION(3, 0) /* version 3.0 of the reserved page */ - -/* the reserved page sizes and offsets */ - -#define XPC_RP_HEADER_SIZE L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page)) - -#define XPC_RP_PART_NASIDS(_rp) ((unsigned long *)((u8 *)(_rp) + \ - XPC_RP_HEADER_SIZE)) -#define XPC_RP_MACH_NASIDS(_rp) (XPC_RP_PART_NASIDS(_rp) + \ - xpc_nasid_mask_nlongs) - - -/* - * The following structure describes the partition's heartbeat info which - * will be periodically read by other partitions to determine whether this - * XPC is still 'alive'. - */ -struct xpc_heartbeat_uv { - unsigned long value; - unsigned long offline; /* if 0, heartbeat should be changing */ -}; - -/* - * Info pertinent to a GRU message queue using a watch list for irq generation. - */ -struct xpc_gru_mq_uv { - void *address; /* address of GRU message queue */ - unsigned int order; /* size of GRU message queue as a power of 2 */ - int irq; /* irq raised when message is received in mq */ - int mmr_blade; /* blade where watchlist was allocated from */ - unsigned long mmr_offset; /* offset of irq mmr located on mmr_blade */ - unsigned long mmr_value; /* value of irq mmr located on mmr_blade */ - int watchlist_num; /* number of watchlist allocatd by BIOS */ - void *gru_mq_desc; /* opaque structure used by the GRU driver */ -}; - -/* - * The activate_mq is used to send/receive GRU messages that affect XPC's - * partition active state and channel state. This is uv only. - */ -struct xpc_activate_mq_msghdr_uv { - unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */ - short partid; /* sender's partid */ - u8 act_state; /* sender's act_state at time msg sent */ - u8 type; /* message's type */ - unsigned long rp_ts_jiffies; /* timestamp of sender's rp setup by XPC */ -}; - -/* activate_mq defined message types */ -#define XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV 0 - -#define XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV 1 -#define XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV 2 - -#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV 3 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV 4 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV 5 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV 6 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV 7 - -#define XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV 8 -#define XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV 9 - -struct xpc_activate_mq_msg_uv { - struct xpc_activate_mq_msghdr_uv hdr; -}; - -struct xpc_activate_mq_msg_activate_req_uv { - struct xpc_activate_mq_msghdr_uv hdr; - unsigned long rp_gpa; - unsigned long heartbeat_gpa; - unsigned long activate_gru_mq_desc_gpa; -}; - -struct xpc_activate_mq_msg_deactivate_req_uv { - struct xpc_activate_mq_msghdr_uv hdr; - enum xp_retval reason; -}; - -struct xpc_activate_mq_msg_chctl_closerequest_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; - enum xp_retval reason; -}; - -struct xpc_activate_mq_msg_chctl_closereply_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; -}; - -struct xpc_activate_mq_msg_chctl_openrequest_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; - short entry_size; /* size of notify_mq's GRU messages */ - short local_nentries; /* ??? Is this needed? What is? */ -}; - -struct xpc_activate_mq_msg_chctl_openreply_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; - short remote_nentries; /* ??? Is this needed? What is? */ - short local_nentries; /* ??? Is this needed? What is? */ - unsigned long notify_gru_mq_desc_gpa; -}; - -struct xpc_activate_mq_msg_chctl_opencomplete_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; -}; - -/* - * Functions registered by add_timer() or called by kernel_thread() only - * allow for a single 64-bit argument. The following macros can be used to - * pack and unpack two (32-bit, 16-bit or 8-bit) arguments into or out from - * the passed argument. - */ -#define XPC_PACK_ARGS(_arg1, _arg2) \ - ((((u64)_arg1) & 0xffffffff) | \ - ((((u64)_arg2) & 0xffffffff) << 32)) - -#define XPC_UNPACK_ARG1(_args) (((u64)_args) & 0xffffffff) -#define XPC_UNPACK_ARG2(_args) ((((u64)_args) >> 32) & 0xffffffff) - -/* - * Define a structure that contains arguments associated with opening and - * closing a channel. - */ -struct xpc_openclose_args { - u16 reason; /* reason why channel is closing */ - u16 entry_size; /* sizeof each message entry */ - u16 remote_nentries; /* #of message entries in remote msg queue */ - u16 local_nentries; /* #of message entries in local msg queue */ - unsigned long local_msgqueue_pa; /* phys addr of local message queue */ -}; - -#define XPC_OPENCLOSE_ARGS_SIZE \ - L1_CACHE_ALIGN(sizeof(struct xpc_openclose_args) * \ - XPC_MAX_NCHANNELS) - - -/* - * Structures to define a fifo singly-linked list. - */ - -struct xpc_fifo_entry_uv { - struct xpc_fifo_entry_uv *next; -}; - -struct xpc_fifo_head_uv { - struct xpc_fifo_entry_uv *first; - struct xpc_fifo_entry_uv *last; - spinlock_t lock; - int n_entries; -}; - -/* - * The format of a uv XPC notify_mq GRU message is as follows: - * - * A user-defined message resides in the payload area. The max size of the - * payload is defined by the user via xpc_connect(). - * - * The size of a message (payload and header) sent via the GRU must be either 1 - * or 2 GRU_CACHE_LINE_BYTES in length. - */ - -struct xpc_notify_mq_msghdr_uv { - union { - unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */ - struct xpc_fifo_entry_uv next; /* FOR XPC INTERNAL USE ONLY */ - } u; - short partid; /* FOR XPC INTERNAL USE ONLY */ - u8 ch_number; /* FOR XPC INTERNAL USE ONLY */ - u8 size; /* FOR XPC INTERNAL USE ONLY */ - unsigned int msg_slot_number; /* FOR XPC INTERNAL USE ONLY */ -}; - -struct xpc_notify_mq_msg_uv { - struct xpc_notify_mq_msghdr_uv hdr; - unsigned long payload; -}; - -/* struct xpc_notify_sn2 type of notification */ - -#define XPC_N_CALL 0x01 /* notify function provided by user */ - -/* - * Define uv's version of the notify entry. It additionally is used to allocate - * a msg slot on the remote partition into which is copied a sent message. - */ -struct xpc_send_msg_slot_uv { - struct xpc_fifo_entry_uv next; - unsigned int msg_slot_number; - xpc_notify_func func; /* user's notify function */ - void *key; /* pointer to user's key */ -}; - -/* - * Define the structure that manages all the stuff required by a channel. In - * particular, they are used to manage the messages sent across the channel. - * - * This structure is private to a partition, and is NOT shared across the - * partition boundary. - * - * There is an array of these structures for each remote partition. It is - * allocated at the time a partition becomes active. The array contains one - * of these structures for each potential channel connection to that partition. - */ - -struct xpc_channel_uv { - void *cached_notify_gru_mq_desc; /* remote partition's notify mq's */ - /* gru mq descriptor */ - - struct xpc_send_msg_slot_uv *send_msg_slots; - void *recv_msg_slots; /* each slot will hold a xpc_notify_mq_msg_uv */ - /* structure plus the user's payload */ - - struct xpc_fifo_head_uv msg_slot_free_list; - struct xpc_fifo_head_uv recv_msg_list; /* deliverable payloads */ -}; - -struct xpc_channel { - short partid; /* ID of remote partition connected */ - spinlock_t lock; /* lock for updating this structure */ - unsigned int flags; /* general flags */ - - enum xp_retval reason; /* reason why channel is disconnect'g */ - int reason_line; /* line# disconnect initiated from */ - - u16 number; /* channel # */ - - u16 entry_size; /* sizeof each msg entry */ - u16 local_nentries; /* #of msg entries in local msg queue */ - u16 remote_nentries; /* #of msg entries in remote msg queue */ - - atomic_t references; /* #of external references to queues */ - - atomic_t n_on_msg_allocate_wq; /* #on msg allocation wait queue */ - wait_queue_head_t msg_allocate_wq; /* msg allocation wait queue */ - - u8 delayed_chctl_flags; /* chctl flags received, but delayed */ - /* action until channel disconnected */ - - atomic_t n_to_notify; /* #of msg senders to notify */ - - xpc_channel_func func; /* user's channel function */ - void *key; /* pointer to user's key */ - - struct completion wdisconnect_wait; /* wait for channel disconnect */ - - /* kthread management related fields */ - - atomic_t kthreads_assigned; /* #of kthreads assigned to channel */ - u32 kthreads_assigned_limit; /* limit on #of kthreads assigned */ - atomic_t kthreads_idle; /* #of kthreads idle waiting for work */ - u32 kthreads_idle_limit; /* limit on #of kthreads idle */ - atomic_t kthreads_active; /* #of kthreads actively working */ - - wait_queue_head_t idle_wq; /* idle kthread wait queue */ - - union { - struct xpc_channel_uv uv; - } sn; - -} ____cacheline_aligned; - -/* struct xpc_channel flags */ - -#define XPC_C_WASCONNECTED 0x00000001 /* channel was connected */ - -#define XPC_C_ROPENCOMPLETE 0x00000002 /* remote open channel complete */ -#define XPC_C_OPENCOMPLETE 0x00000004 /* local open channel complete */ -#define XPC_C_ROPENREPLY 0x00000008 /* remote open channel reply */ -#define XPC_C_OPENREPLY 0x00000010 /* local open channel reply */ -#define XPC_C_ROPENREQUEST 0x00000020 /* remote open channel request */ -#define XPC_C_OPENREQUEST 0x00000040 /* local open channel request */ - -#define XPC_C_SETUP 0x00000080 /* channel's msgqueues are alloc'd */ -#define XPC_C_CONNECTEDCALLOUT 0x00000100 /* connected callout initiated */ -#define XPC_C_CONNECTEDCALLOUT_MADE \ - 0x00000200 /* connected callout completed */ -#define XPC_C_CONNECTED 0x00000400 /* local channel is connected */ -#define XPC_C_CONNECTING 0x00000800 /* channel is being connected */ - -#define XPC_C_RCLOSEREPLY 0x00001000 /* remote close channel reply */ -#define XPC_C_CLOSEREPLY 0x00002000 /* local close channel reply */ -#define XPC_C_RCLOSEREQUEST 0x00004000 /* remote close channel request */ -#define XPC_C_CLOSEREQUEST 0x00008000 /* local close channel request */ - -#define XPC_C_DISCONNECTED 0x00010000 /* channel is disconnected */ -#define XPC_C_DISCONNECTING 0x00020000 /* channel is being disconnected */ -#define XPC_C_DISCONNECTINGCALLOUT \ - 0x00040000 /* disconnecting callout initiated */ -#define XPC_C_DISCONNECTINGCALLOUT_MADE \ - 0x00080000 /* disconnecting callout completed */ -#define XPC_C_WDISCONNECT 0x00100000 /* waiting for channel disconnect */ - -/* - * The channel control flags (chctl) union consists of a 64-bit variable which - * is divided up into eight bytes, ordered from right to left. Byte zero - * pertains to channel 0, byte one to channel 1, and so on. Each channel's byte - * can have one or more of the chctl flags set in it. - */ - -union xpc_channel_ctl_flags { - u64 all_flags; - u8 flags[XPC_MAX_NCHANNELS]; -}; - -/* chctl flags */ -#define XPC_CHCTL_CLOSEREQUEST 0x01 -#define XPC_CHCTL_CLOSEREPLY 0x02 -#define XPC_CHCTL_OPENREQUEST 0x04 -#define XPC_CHCTL_OPENREPLY 0x08 -#define XPC_CHCTL_OPENCOMPLETE 0x10 -#define XPC_CHCTL_MSGREQUEST 0x20 - -#define XPC_OPENCLOSE_CHCTL_FLAGS \ - (XPC_CHCTL_CLOSEREQUEST | XPC_CHCTL_CLOSEREPLY | \ - XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | \ - XPC_CHCTL_OPENCOMPLETE) -#define XPC_MSG_CHCTL_FLAGS XPC_CHCTL_MSGREQUEST - -static inline int -xpc_any_openclose_chctl_flags_set(union xpc_channel_ctl_flags *chctl) -{ - int ch_number; - - for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) { - if (chctl->flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) - return 1; - } - return 0; -} - -static inline int -xpc_any_msg_chctl_flags_set(union xpc_channel_ctl_flags *chctl) -{ - int ch_number; - - for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) { - if (chctl->flags[ch_number] & XPC_MSG_CHCTL_FLAGS) - return 1; - } - return 0; -} - -struct xpc_partition_uv { - unsigned long heartbeat_gpa; /* phys addr of partition's heartbeat */ - struct xpc_heartbeat_uv cached_heartbeat; /* cached copy of */ - /* partition's heartbeat */ - unsigned long activate_gru_mq_desc_gpa; /* phys addr of parititon's */ - /* activate mq's gru mq */ - /* descriptor */ - void *cached_activate_gru_mq_desc; /* cached copy of partition's */ - /* activate mq's gru mq descriptor */ - struct mutex cached_activate_gru_mq_desc_mutex; - spinlock_t flags_lock; /* protect updating of flags */ - unsigned int flags; /* general flags */ - u8 remote_act_state; /* remote partition's act_state */ - u8 act_state_req; /* act_state request from remote partition */ - enum xp_retval reason; /* reason for deactivate act_state request */ -}; - -/* struct xpc_partition_uv flags */ - -#define XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV 0x00000001 -#define XPC_P_ENGAGED_UV 0x00000002 - -/* struct xpc_partition_uv act_state change requests */ - -#define XPC_P_ASR_ACTIVATE_UV 0x01 -#define XPC_P_ASR_REACTIVATE_UV 0x02 -#define XPC_P_ASR_DEACTIVATE_UV 0x03 - -struct xpc_partition { - - /* XPC HB infrastructure */ - - u8 remote_rp_version; /* version# of partition's rsvd pg */ - unsigned long remote_rp_ts_jiffies; /* timestamp when rsvd pg setup */ - unsigned long remote_rp_pa; /* phys addr of partition's rsvd pg */ - u64 last_heartbeat; /* HB at last read */ - u32 activate_IRQ_rcvd; /* IRQs since activation */ - spinlock_t act_lock; /* protect updating of act_state */ - u8 act_state; /* from XPC HB viewpoint */ - enum xp_retval reason; /* reason partition is deactivating */ - int reason_line; /* line# deactivation initiated from */ - - unsigned long disengage_timeout; /* timeout in jiffies */ - struct timer_list disengage_timer; - - /* XPC infrastructure referencing and teardown control */ - - u8 setup_state; /* infrastructure setup state */ - wait_queue_head_t teardown_wq; /* kthread waiting to teardown infra */ - atomic_t references; /* #of references to infrastructure */ - - u8 nchannels; /* #of defined channels supported */ - atomic_t nchannels_active; /* #of channels that are not DISCONNECTED */ - atomic_t nchannels_engaged; /* #of channels engaged with remote part */ - struct xpc_channel *channels; /* array of channel structures */ - - /* fields used for managing channel avialability and activity */ - - union xpc_channel_ctl_flags chctl; /* chctl flags yet to be processed */ - spinlock_t chctl_lock; /* chctl flags lock */ - - void *remote_openclose_args_base; /* base address of kmalloc'd space */ - struct xpc_openclose_args *remote_openclose_args; /* copy of remote's */ - /* args */ - - /* channel manager related fields */ - - atomic_t channel_mgr_requests; /* #of requests to activate chan mgr */ - wait_queue_head_t channel_mgr_wq; /* channel mgr's wait queue */ - - union { - struct xpc_partition_uv uv; - } sn; - -} ____cacheline_aligned; - -struct xpc_arch_operations { - int (*setup_partitions) (void); - void (*teardown_partitions) (void); - void (*process_activate_IRQ_rcvd) (void); - enum xp_retval (*get_partition_rsvd_page_pa) - (void *, u64 *, unsigned long *, size_t *); - int (*setup_rsvd_page) (struct xpc_rsvd_page *); - - void (*allow_hb) (short); - void (*disallow_hb) (short); - void (*disallow_all_hbs) (void); - void (*increment_heartbeat) (void); - void (*offline_heartbeat) (void); - void (*online_heartbeat) (void); - void (*heartbeat_init) (void); - void (*heartbeat_exit) (void); - enum xp_retval (*get_remote_heartbeat) (struct xpc_partition *); - - void (*request_partition_activation) (struct xpc_rsvd_page *, - unsigned long, int); - void (*request_partition_reactivation) (struct xpc_partition *); - void (*request_partition_deactivation) (struct xpc_partition *); - void (*cancel_partition_deactivation_request) (struct xpc_partition *); - enum xp_retval (*setup_ch_structures) (struct xpc_partition *); - void (*teardown_ch_structures) (struct xpc_partition *); - - enum xp_retval (*make_first_contact) (struct xpc_partition *); - - u64 (*get_chctl_all_flags) (struct xpc_partition *); - void (*send_chctl_closerequest) (struct xpc_channel *, unsigned long *); - void (*send_chctl_closereply) (struct xpc_channel *, unsigned long *); - void (*send_chctl_openrequest) (struct xpc_channel *, unsigned long *); - void (*send_chctl_openreply) (struct xpc_channel *, unsigned long *); - void (*send_chctl_opencomplete) (struct xpc_channel *, unsigned long *); - void (*process_msg_chctl_flags) (struct xpc_partition *, int); - - enum xp_retval (*save_remote_msgqueue_pa) (struct xpc_channel *, - unsigned long); - - enum xp_retval (*setup_msg_structures) (struct xpc_channel *); - void (*teardown_msg_structures) (struct xpc_channel *); - - void (*indicate_partition_engaged) (struct xpc_partition *); - void (*indicate_partition_disengaged) (struct xpc_partition *); - void (*assume_partition_disengaged) (short); - int (*partition_engaged) (short); - int (*any_partition_engaged) (void); - - int (*n_of_deliverable_payloads) (struct xpc_channel *); - enum xp_retval (*send_payload) (struct xpc_channel *, u32, void *, - u16, u8, xpc_notify_func, void *); - void *(*get_deliverable_payload) (struct xpc_channel *); - void (*received_payload) (struct xpc_channel *, void *); - void (*notify_senders_of_disconnect) (struct xpc_channel *); -}; - -/* struct xpc_partition act_state values (for XPC HB) */ - -#define XPC_P_AS_INACTIVE 0x00 /* partition is not active */ -#define XPC_P_AS_ACTIVATION_REQ 0x01 /* created thread to activate */ -#define XPC_P_AS_ACTIVATING 0x02 /* activation thread started */ -#define XPC_P_AS_ACTIVE 0x03 /* xpc_partition_up() was called */ -#define XPC_P_AS_DEACTIVATING 0x04 /* partition deactivation initiated */ - -#define XPC_DEACTIVATE_PARTITION(_p, _reason) \ - xpc_deactivate_partition(__LINE__, (_p), (_reason)) - -/* struct xpc_partition setup_state values */ - -#define XPC_P_SS_UNSET 0x00 /* infrastructure was never setup */ -#define XPC_P_SS_SETUP 0x01 /* infrastructure is setup */ -#define XPC_P_SS_WTEARDOWN 0x02 /* waiting to teardown infrastructure */ -#define XPC_P_SS_TORNDOWN 0x03 /* infrastructure is torndown */ - -/* number of seconds to wait for other partitions to disengage */ -#define XPC_DISENGAGE_DEFAULT_TIMELIMIT 90 - -/* interval in seconds to print 'waiting deactivation' messages */ -#define XPC_DEACTIVATE_PRINTMSG_INTERVAL 10 - -#define XPC_PARTID(_p) ((short)((_p) - &xpc_partitions[0])) - -/* found in xp_main.c */ -extern struct xpc_registration xpc_registrations[]; - -/* found in xpc_main.c */ -extern struct device *xpc_part; -extern struct device *xpc_chan; -extern struct xpc_arch_operations xpc_arch_ops; -extern int xpc_disengage_timelimit; -extern int xpc_disengage_timedout; -extern int xpc_activate_IRQ_rcvd; -extern spinlock_t xpc_activate_IRQ_rcvd_lock; -extern wait_queue_head_t xpc_activate_IRQ_wq; -extern void *xpc_kzalloc_cacheline_aligned(size_t, gfp_t, void **); -extern void xpc_activate_partition(struct xpc_partition *); -extern void xpc_activate_kthreads(struct xpc_channel *, int); -extern void xpc_create_kthreads(struct xpc_channel *, int, int); -extern void xpc_disconnect_wait(int); - -/* found in xpc_uv.c */ -extern int xpc_init_uv(void); -extern void xpc_exit_uv(void); - -/* found in xpc_partition.c */ -extern int xpc_exiting; -extern int xpc_nasid_mask_nlongs; -extern struct xpc_rsvd_page *xpc_rsvd_page; -extern unsigned long *xpc_mach_nasids; -extern struct xpc_partition *xpc_partitions; -extern void *xpc_kmalloc_cacheline_aligned(size_t, gfp_t, void **); -extern int xpc_setup_rsvd_page(void); -extern void xpc_teardown_rsvd_page(void); -extern int xpc_identify_activate_IRQ_sender(void); -extern int xpc_partition_disengaged(struct xpc_partition *); -extern int xpc_partition_disengaged_from_timer(struct xpc_partition *part); -extern enum xp_retval xpc_mark_partition_active(struct xpc_partition *); -extern void xpc_mark_partition_inactive(struct xpc_partition *); -extern void xpc_discovery(void); -extern enum xp_retval xpc_get_remote_rp(int, unsigned long *, - struct xpc_rsvd_page *, - unsigned long *); -extern void xpc_deactivate_partition(const int, struct xpc_partition *, - enum xp_retval); -extern enum xp_retval xpc_initiate_partid_to_nasids(short, void *); - -/* found in xpc_channel.c */ -extern void xpc_initiate_connect(int); -extern void xpc_initiate_disconnect(int); -extern enum xp_retval xpc_allocate_msg_wait(struct xpc_channel *); -extern enum xp_retval xpc_initiate_send(short, int, u32, void *, u16); -extern enum xp_retval xpc_initiate_send_notify(short, int, u32, void *, u16, - xpc_notify_func, void *); -extern void xpc_initiate_received(short, int, void *); -extern void xpc_process_sent_chctl_flags(struct xpc_partition *); -extern void xpc_connected_callout(struct xpc_channel *); -extern void xpc_deliver_payload(struct xpc_channel *); -extern void xpc_disconnect_channel(const int, struct xpc_channel *, - enum xp_retval, unsigned long *); -extern void xpc_disconnect_callout(struct xpc_channel *, enum xp_retval); -extern void xpc_partition_going_down(struct xpc_partition *, enum xp_retval); - -static inline void -xpc_wakeup_channel_mgr(struct xpc_partition *part) -{ - if (atomic_inc_return(&part->channel_mgr_requests) == 1) - wake_up(&part->channel_mgr_wq); -} - -/* - * These next two inlines are used to keep us from tearing down a channel's - * msg queues while a thread may be referencing them. - */ -static inline void -xpc_msgqueue_ref(struct xpc_channel *ch) -{ - atomic_inc(&ch->references); -} - -static inline void -xpc_msgqueue_deref(struct xpc_channel *ch) -{ - s32 refs = atomic_dec_return(&ch->references); - - DBUG_ON(refs < 0); - if (refs == 0) - xpc_wakeup_channel_mgr(&xpc_partitions[ch->partid]); -} - -#define XPC_DISCONNECT_CHANNEL(_ch, _reason, _irqflgs) \ - xpc_disconnect_channel(__LINE__, _ch, _reason, _irqflgs) - -/* - * These two inlines are used to keep us from tearing down a partition's - * setup infrastructure while a thread may be referencing it. - */ -static inline void -xpc_part_deref(struct xpc_partition *part) -{ - s32 refs = atomic_dec_return(&part->references); - - DBUG_ON(refs < 0); - if (refs == 0 && part->setup_state == XPC_P_SS_WTEARDOWN) - wake_up(&part->teardown_wq); -} - -static inline int -xpc_part_ref(struct xpc_partition *part) -{ - int setup; - - atomic_inc(&part->references); - setup = (part->setup_state == XPC_P_SS_SETUP); - if (!setup) - xpc_part_deref(part); - - return setup; -} - -/* - * The following macro is to be used for the setting of the reason and - * reason_line fields in both the struct xpc_channel and struct xpc_partition - * structures. - */ -#define XPC_SET_REASON(_p, _reason, _line) \ - { \ - (_p)->reason = _reason; \ - (_p)->reason_line = _line; \ - } - -#endif /* _DRIVERS_MISC_SGIXP_XPC_H */ diff --git a/drivers/misc/sgi-xp/xpc_channel.c b/drivers/misc/sgi-xp/xpc_channel.c deleted file mode 100644 index 8e6607fc8a67..000000000000 --- a/drivers/misc/sgi-xp/xpc_channel.c +++ /dev/null @@ -1,1011 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) channel support. - * - * This is the part of XPC that manages the channels and - * sends/receives messages across them to/from other partitions. - * - */ - -#include <linux/device.h> -#include "xpc.h" - -/* - * Process a connect message from a remote partition. - * - * Note: xpc_process_connect() is expecting to be called with the - * spin_lock_irqsave held and will leave it locked upon return. - */ -static void -xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags) -{ - enum xp_retval ret; - - lockdep_assert_held(&ch->lock); - - if (!(ch->flags & XPC_C_OPENREQUEST) || - !(ch->flags & XPC_C_ROPENREQUEST)) { - /* nothing more to do for now */ - return; - } - DBUG_ON(!(ch->flags & XPC_C_CONNECTING)); - - if (!(ch->flags & XPC_C_SETUP)) { - spin_unlock_irqrestore(&ch->lock, *irq_flags); - ret = xpc_arch_ops.setup_msg_structures(ch); - spin_lock_irqsave(&ch->lock, *irq_flags); - - if (ret != xpSuccess) - XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags); - else - ch->flags |= XPC_C_SETUP; - - if (ch->flags & XPC_C_DISCONNECTING) - return; - } - - if (!(ch->flags & XPC_C_OPENREPLY)) { - ch->flags |= XPC_C_OPENREPLY; - xpc_arch_ops.send_chctl_openreply(ch, irq_flags); - } - - if (!(ch->flags & XPC_C_ROPENREPLY)) - return; - - if (!(ch->flags & XPC_C_OPENCOMPLETE)) { - ch->flags |= (XPC_C_OPENCOMPLETE | XPC_C_CONNECTED); - xpc_arch_ops.send_chctl_opencomplete(ch, irq_flags); - } - - if (!(ch->flags & XPC_C_ROPENCOMPLETE)) - return; - - dev_info(xpc_chan, "channel %d to partition %d connected\n", - ch->number, ch->partid); - - ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */ -} - -/* - * spin_lock_irqsave() is expected to be held on entry. - */ -static void -xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_partition *part = &xpc_partitions[ch->partid]; - u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED); - - lockdep_assert_held(&ch->lock); - - if (!(ch->flags & XPC_C_DISCONNECTING)) - return; - - DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); - - /* make sure all activity has settled down first */ - - if (atomic_read(&ch->kthreads_assigned) > 0 || - atomic_read(&ch->references) > 0) { - return; - } - DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && - !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE)); - - if (part->act_state == XPC_P_AS_DEACTIVATING) { - /* can't proceed until the other side disengages from us */ - if (xpc_arch_ops.partition_engaged(ch->partid)) - return; - - } else { - - /* as long as the other side is up do the full protocol */ - - if (!(ch->flags & XPC_C_RCLOSEREQUEST)) - return; - - if (!(ch->flags & XPC_C_CLOSEREPLY)) { - ch->flags |= XPC_C_CLOSEREPLY; - xpc_arch_ops.send_chctl_closereply(ch, irq_flags); - } - - if (!(ch->flags & XPC_C_RCLOSEREPLY)) - return; - } - - /* wake those waiting for notify completion */ - if (atomic_read(&ch->n_to_notify) > 0) { - /* we do callout while holding ch->lock, callout can't block */ - xpc_arch_ops.notify_senders_of_disconnect(ch); - } - - /* both sides are disconnected now */ - - if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) { - spin_unlock_irqrestore(&ch->lock, *irq_flags); - xpc_disconnect_callout(ch, xpDisconnected); - spin_lock_irqsave(&ch->lock, *irq_flags); - } - - DBUG_ON(atomic_read(&ch->n_to_notify) != 0); - - /* it's now safe to free the channel's message queues */ - xpc_arch_ops.teardown_msg_structures(ch); - - ch->func = NULL; - ch->key = NULL; - ch->entry_size = 0; - ch->local_nentries = 0; - ch->remote_nentries = 0; - ch->kthreads_assigned_limit = 0; - ch->kthreads_idle_limit = 0; - - /* - * Mark the channel disconnected and clear all other flags, including - * XPC_C_SETUP (because of call to - * xpc_arch_ops.teardown_msg_structures()) but not including - * XPC_C_WDISCONNECT (if it was set). - */ - ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT)); - - atomic_dec(&part->nchannels_active); - - if (channel_was_connected) { - dev_info(xpc_chan, "channel %d to partition %d disconnected, " - "reason=%d\n", ch->number, ch->partid, ch->reason); - } - - if (ch->flags & XPC_C_WDISCONNECT) { - /* we won't lose the CPU since we're holding ch->lock */ - complete(&ch->wdisconnect_wait); - } else if (ch->delayed_chctl_flags) { - if (part->act_state != XPC_P_AS_DEACTIVATING) { - /* time to take action on any delayed chctl flags */ - spin_lock(&part->chctl_lock); - part->chctl.flags[ch->number] |= - ch->delayed_chctl_flags; - spin_unlock(&part->chctl_lock); - } - ch->delayed_chctl_flags = 0; - } -} - -/* - * Process a change in the channel's remote connection state. - */ -static void -xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number, - u8 chctl_flags) -{ - unsigned long irq_flags; - struct xpc_openclose_args *args = - &part->remote_openclose_args[ch_number]; - struct xpc_channel *ch = &part->channels[ch_number]; - enum xp_retval reason; - enum xp_retval ret; - int create_kthread = 0; - - spin_lock_irqsave(&ch->lock, irq_flags); - -again: - - if ((ch->flags & XPC_C_DISCONNECTED) && - (ch->flags & XPC_C_WDISCONNECT)) { - /* - * Delay processing chctl flags until thread waiting disconnect - * has had a chance to see that the channel is disconnected. - */ - ch->delayed_chctl_flags |= chctl_flags; - goto out; - } - - if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) { - - dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received " - "from partid=%d, channel=%d\n", args->reason, - ch->partid, ch->number); - - /* - * If RCLOSEREQUEST is set, we're probably waiting for - * RCLOSEREPLY. We should find it and a ROPENREQUEST packed - * with this RCLOSEREQUEST in the chctl_flags. - */ - - if (ch->flags & XPC_C_RCLOSEREQUEST) { - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING)); - DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); - DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY)); - DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY); - - DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY)); - chctl_flags &= ~XPC_CHCTL_CLOSEREPLY; - ch->flags |= XPC_C_RCLOSEREPLY; - - /* both sides have finished disconnecting */ - xpc_process_disconnect(ch, &irq_flags); - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); - goto again; - } - - if (ch->flags & XPC_C_DISCONNECTED) { - if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) { - if (part->chctl.flags[ch_number] & - XPC_CHCTL_OPENREQUEST) { - - DBUG_ON(ch->delayed_chctl_flags != 0); - spin_lock(&part->chctl_lock); - part->chctl.flags[ch_number] |= - XPC_CHCTL_CLOSEREQUEST; - spin_unlock(&part->chctl_lock); - } - goto out; - } - - XPC_SET_REASON(ch, 0, 0); - ch->flags &= ~XPC_C_DISCONNECTED; - - atomic_inc(&part->nchannels_active); - ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST); - } - - chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | - XPC_CHCTL_OPENCOMPLETE); - - /* - * The meaningful CLOSEREQUEST connection state fields are: - * reason = reason connection is to be closed - */ - - ch->flags |= XPC_C_RCLOSEREQUEST; - - if (!(ch->flags & XPC_C_DISCONNECTING)) { - reason = args->reason; - if (reason <= xpSuccess || reason > xpUnknownReason) - reason = xpUnknownReason; - else if (reason == xpUnregistering) - reason = xpOtherUnregistering; - - XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); - - DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY); - goto out; - } - - xpc_process_disconnect(ch, &irq_flags); - } - - if (chctl_flags & XPC_CHCTL_CLOSEREPLY) { - - dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid=" - "%d, channel=%d\n", ch->partid, ch->number); - - if (ch->flags & XPC_C_DISCONNECTED) { - DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING); - goto out; - } - - DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); - - if (!(ch->flags & XPC_C_RCLOSEREQUEST)) { - if (part->chctl.flags[ch_number] & - XPC_CHCTL_CLOSEREQUEST) { - - DBUG_ON(ch->delayed_chctl_flags != 0); - spin_lock(&part->chctl_lock); - part->chctl.flags[ch_number] |= - XPC_CHCTL_CLOSEREPLY; - spin_unlock(&part->chctl_lock); - } - goto out; - } - - ch->flags |= XPC_C_RCLOSEREPLY; - - if (ch->flags & XPC_C_CLOSEREPLY) { - /* both sides have finished disconnecting */ - xpc_process_disconnect(ch, &irq_flags); - } - } - - if (chctl_flags & XPC_CHCTL_OPENREQUEST) { - - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, " - "local_nentries=%d) received from partid=%d, " - "channel=%d\n", args->entry_size, args->local_nentries, - ch->partid, ch->number); - - if (part->act_state == XPC_P_AS_DEACTIVATING || - (ch->flags & XPC_C_ROPENREQUEST)) { - goto out; - } - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) { - ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST; - goto out; - } - DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED | - XPC_C_OPENREQUEST))); - DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | - XPC_C_OPENREPLY | XPC_C_CONNECTED)); - - /* - * The meaningful OPENREQUEST connection state fields are: - * entry_size = size of channel's messages in bytes - * local_nentries = remote partition's local_nentries - */ - if (args->entry_size == 0 || args->local_nentries == 0) { - /* assume OPENREQUEST was delayed by mistake */ - goto out; - } - - ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING); - ch->remote_nentries = args->local_nentries; - - if (ch->flags & XPC_C_OPENREQUEST) { - if (args->entry_size != ch->entry_size) { - XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, - &irq_flags); - goto out; - } - } else { - ch->entry_size = args->entry_size; - - XPC_SET_REASON(ch, 0, 0); - ch->flags &= ~XPC_C_DISCONNECTED; - - atomic_inc(&part->nchannels_active); - } - - xpc_process_connect(ch, &irq_flags); - } - - if (chctl_flags & XPC_CHCTL_OPENREPLY) { - - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa=" - "0x%lx, local_nentries=%d, remote_nentries=%d) " - "received from partid=%d, channel=%d\n", - args->local_msgqueue_pa, args->local_nentries, - args->remote_nentries, ch->partid, ch->number); - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) - goto out; - - if (!(ch->flags & XPC_C_OPENREQUEST)) { - XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, - &irq_flags); - goto out; - } - - DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); - DBUG_ON(ch->flags & XPC_C_CONNECTED); - - /* - * The meaningful OPENREPLY connection state fields are: - * local_msgqueue_pa = physical address of remote - * partition's local_msgqueue - * local_nentries = remote partition's local_nentries - * remote_nentries = remote partition's remote_nentries - */ - DBUG_ON(args->local_msgqueue_pa == 0); - DBUG_ON(args->local_nentries == 0); - DBUG_ON(args->remote_nentries == 0); - - ret = xpc_arch_ops.save_remote_msgqueue_pa(ch, - args->local_msgqueue_pa); - if (ret != xpSuccess) { - XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags); - goto out; - } - ch->flags |= XPC_C_ROPENREPLY; - - if (args->local_nentries < ch->remote_nentries) { - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " - "remote_nentries=%d, old remote_nentries=%d, " - "partid=%d, channel=%d\n", - args->local_nentries, ch->remote_nentries, - ch->partid, ch->number); - - ch->remote_nentries = args->local_nentries; - } - if (args->remote_nentries < ch->local_nentries) { - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " - "local_nentries=%d, old local_nentries=%d, " - "partid=%d, channel=%d\n", - args->remote_nentries, ch->local_nentries, - ch->partid, ch->number); - - ch->local_nentries = args->remote_nentries; - } - - xpc_process_connect(ch, &irq_flags); - } - - if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) { - - dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from " - "partid=%d, channel=%d\n", ch->partid, ch->number); - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) - goto out; - - if (!(ch->flags & XPC_C_OPENREQUEST) || - !(ch->flags & XPC_C_OPENREPLY)) { - XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, - &irq_flags); - goto out; - } - - DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); - DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY)); - DBUG_ON(!(ch->flags & XPC_C_CONNECTED)); - - ch->flags |= XPC_C_ROPENCOMPLETE; - - xpc_process_connect(ch, &irq_flags); - create_kthread = 1; - } - -out: - spin_unlock_irqrestore(&ch->lock, irq_flags); - - if (create_kthread) - xpc_create_kthreads(ch, 1, 0); -} - -/* - * Attempt to establish a channel connection to a remote partition. - */ -static enum xp_retval -xpc_connect_channel(struct xpc_channel *ch) -{ - unsigned long irq_flags; - struct xpc_registration *registration = &xpc_registrations[ch->number]; - - if (mutex_trylock(®istration->mutex) == 0) - return xpRetry; - - if (!XPC_CHANNEL_REGISTERED(ch->number)) { - mutex_unlock(®istration->mutex); - return xpUnregistered; - } - - spin_lock_irqsave(&ch->lock, irq_flags); - - DBUG_ON(ch->flags & XPC_C_CONNECTED); - DBUG_ON(ch->flags & XPC_C_OPENREQUEST); - - if (ch->flags & XPC_C_DISCONNECTING) { - spin_unlock_irqrestore(&ch->lock, irq_flags); - mutex_unlock(®istration->mutex); - return ch->reason; - } - - /* add info from the channel connect registration to the channel */ - - ch->kthreads_assigned_limit = registration->assigned_limit; - ch->kthreads_idle_limit = registration->idle_limit; - DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0); - DBUG_ON(atomic_read(&ch->kthreads_idle) != 0); - DBUG_ON(atomic_read(&ch->kthreads_active) != 0); - - ch->func = registration->func; - DBUG_ON(registration->func == NULL); - ch->key = registration->key; - - ch->local_nentries = registration->nentries; - - if (ch->flags & XPC_C_ROPENREQUEST) { - if (registration->entry_size != ch->entry_size) { - /* the local and remote sides aren't the same */ - - /* - * Because XPC_DISCONNECT_CHANNEL() can block we're - * forced to up the registration sema before we unlock - * the channel lock. But that's okay here because we're - * done with the part that required the registration - * sema. XPC_DISCONNECT_CHANNEL() requires that the - * channel lock be locked and will unlock and relock - * the channel lock as needed. - */ - mutex_unlock(®istration->mutex); - XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, - &irq_flags); - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpUnequalMsgSizes; - } - } else { - ch->entry_size = registration->entry_size; - - XPC_SET_REASON(ch, 0, 0); - ch->flags &= ~XPC_C_DISCONNECTED; - - atomic_inc(&xpc_partitions[ch->partid].nchannels_active); - } - - mutex_unlock(®istration->mutex); - - /* initiate the connection */ - - ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING); - xpc_arch_ops.send_chctl_openrequest(ch, &irq_flags); - - xpc_process_connect(ch, &irq_flags); - - spin_unlock_irqrestore(&ch->lock, irq_flags); - - return xpSuccess; -} - -void -xpc_process_sent_chctl_flags(struct xpc_partition *part) -{ - unsigned long irq_flags; - union xpc_channel_ctl_flags chctl; - struct xpc_channel *ch; - int ch_number; - u32 ch_flags; - - chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part); - - /* - * Initiate channel connections for registered channels. - * - * For each connected channel that has pending messages activate idle - * kthreads and/or create new kthreads as needed. - */ - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch = &part->channels[ch_number]; - - /* - * Process any open or close related chctl flags, and then deal - * with connecting or disconnecting the channel as required. - */ - - if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) { - xpc_process_openclose_chctl_flags(part, ch_number, - chctl.flags[ch_number]); - } - - ch_flags = ch->flags; /* need an atomic snapshot of flags */ - - if (ch_flags & XPC_C_DISCONNECTING) { - spin_lock_irqsave(&ch->lock, irq_flags); - xpc_process_disconnect(ch, &irq_flags); - spin_unlock_irqrestore(&ch->lock, irq_flags); - continue; - } - - if (part->act_state == XPC_P_AS_DEACTIVATING) - continue; - - if (!(ch_flags & XPC_C_CONNECTED)) { - if (!(ch_flags & XPC_C_OPENREQUEST)) { - DBUG_ON(ch_flags & XPC_C_SETUP); - (void)xpc_connect_channel(ch); - } - continue; - } - - /* - * Process any message related chctl flags, this may involve - * the activation of kthreads to deliver any pending messages - * sent from the other partition. - */ - - if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS) - xpc_arch_ops.process_msg_chctl_flags(part, ch_number); - } -} - -/* - * XPC's heartbeat code calls this function to inform XPC that a partition is - * going down. XPC responds by tearing down the XPartition Communication - * infrastructure used for the just downed partition. - * - * XPC's heartbeat code will never call this function and xpc_partition_up() - * at the same time. Nor will it ever make multiple calls to either function - * at the same time. - */ -void -xpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason) -{ - unsigned long irq_flags; - int ch_number; - struct xpc_channel *ch; - - dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n", - XPC_PARTID(part), reason); - - if (!xpc_part_ref(part)) { - /* infrastructure for this partition isn't currently set up */ - return; - } - - /* disconnect channels associated with the partition going down */ - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch = &part->channels[ch_number]; - - xpc_msgqueue_ref(ch); - spin_lock_irqsave(&ch->lock, irq_flags); - - XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); - - spin_unlock_irqrestore(&ch->lock, irq_flags); - xpc_msgqueue_deref(ch); - } - - xpc_wakeup_channel_mgr(part); - - xpc_part_deref(part); -} - -/* - * Called by XP at the time of channel connection registration to cause - * XPC to establish connections to all currently active partitions. - */ -void -xpc_initiate_connect(int ch_number) -{ - short partid; - struct xpc_partition *part; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_part_ref(part)) { - /* - * Initiate the establishment of a connection on the - * newly registered channel to the remote partition. - */ - xpc_wakeup_channel_mgr(part); - xpc_part_deref(part); - } - } -} - -void -xpc_connected_callout(struct xpc_channel *ch) -{ - /* let the registerer know that a connection has been established */ - - if (ch->func != NULL) { - dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, " - "partid=%d, channel=%d\n", ch->partid, ch->number); - - ch->func(xpConnected, ch->partid, ch->number, - (void *)(u64)ch->local_nentries, ch->key); - - dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, " - "partid=%d, channel=%d\n", ch->partid, ch->number); - } -} - -/* - * Called by XP at the time of channel connection unregistration to cause - * XPC to teardown all current connections for the specified channel. - * - * Before returning xpc_initiate_disconnect() will wait until all connections - * on the specified channel have been closed/torndown. So the caller can be - * assured that they will not be receiving any more callouts from XPC to the - * function they registered via xpc_connect(). - * - * Arguments: - * - * ch_number - channel # to unregister. - */ -void -xpc_initiate_disconnect(int ch_number) -{ - unsigned long irq_flags; - short partid; - struct xpc_partition *part; - struct xpc_channel *ch; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - - /* initiate the channel disconnect for every active partition */ - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_part_ref(part)) { - ch = &part->channels[ch_number]; - xpc_msgqueue_ref(ch); - - spin_lock_irqsave(&ch->lock, irq_flags); - - if (!(ch->flags & XPC_C_DISCONNECTED)) { - ch->flags |= XPC_C_WDISCONNECT; - - XPC_DISCONNECT_CHANNEL(ch, xpUnregistering, - &irq_flags); - } - - spin_unlock_irqrestore(&ch->lock, irq_flags); - - xpc_msgqueue_deref(ch); - xpc_part_deref(part); - } - } - - xpc_disconnect_wait(ch_number); -} - -/* - * To disconnect a channel, and reflect it back to all who may be waiting. - * - * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by - * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by - * xpc_disconnect_wait(). - * - * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN. - */ -void -xpc_disconnect_channel(const int line, struct xpc_channel *ch, - enum xp_retval reason, unsigned long *irq_flags) -{ - u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED); - - lockdep_assert_held(&ch->lock); - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) - return; - - DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED))); - - dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n", - reason, line, ch->partid, ch->number); - - XPC_SET_REASON(ch, reason, line); - - ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING); - /* some of these may not have been set */ - ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY | - XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | - XPC_C_CONNECTING | XPC_C_CONNECTED); - - xpc_arch_ops.send_chctl_closerequest(ch, irq_flags); - - if (channel_was_connected) - ch->flags |= XPC_C_WASCONNECTED; - - spin_unlock_irqrestore(&ch->lock, *irq_flags); - - /* wake all idle kthreads so they can exit */ - if (atomic_read(&ch->kthreads_idle) > 0) { - wake_up_all(&ch->idle_wq); - - } else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && - !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) { - /* start a kthread that will do the xpDisconnecting callout */ - xpc_create_kthreads(ch, 1, 1); - } - - /* wake those waiting to allocate an entry from the local msg queue */ - if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) - wake_up(&ch->msg_allocate_wq); - - spin_lock_irqsave(&ch->lock, *irq_flags); -} - -void -xpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason) -{ - /* - * Let the channel's registerer know that the channel is being - * disconnected. We don't want to do this if the registerer was never - * informed of a connection being made. - */ - - if (ch->func != NULL) { - dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, " - "channel=%d\n", reason, ch->partid, ch->number); - - ch->func(reason, ch->partid, ch->number, NULL, ch->key); - - dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, " - "channel=%d\n", reason, ch->partid, ch->number); - } -} - -/* - * Wait for a message entry to become available for the specified channel, - * but don't wait any longer than 1 jiffy. - */ -enum xp_retval -xpc_allocate_msg_wait(struct xpc_channel *ch) -{ - enum xp_retval ret; - DEFINE_WAIT(wait); - - if (ch->flags & XPC_C_DISCONNECTING) { - DBUG_ON(ch->reason == xpInterrupted); - return ch->reason; - } - - atomic_inc(&ch->n_on_msg_allocate_wq); - prepare_to_wait(&ch->msg_allocate_wq, &wait, TASK_INTERRUPTIBLE); - ret = schedule_timeout(1); - finish_wait(&ch->msg_allocate_wq, &wait); - atomic_dec(&ch->n_on_msg_allocate_wq); - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - DBUG_ON(ch->reason == xpInterrupted); - } else if (ret == 0) { - ret = xpTimeout; - } else { - ret = xpInterrupted; - } - - return ret; -} - -/* - * Send a message that contains the user's payload on the specified channel - * connected to the specified partition. - * - * NOTE that this routine can sleep waiting for a message entry to become - * available. To not sleep, pass in the XPC_NOWAIT flag. - * - * Once sent, this routine will not wait for the message to be received, nor - * will notification be given when it does happen. - * - * Arguments: - * - * partid - ID of partition to which the channel is connected. - * ch_number - channel # to send message on. - * flags - see xp.h for valid flags. - * payload - pointer to the payload which is to be sent. - * payload_size - size of the payload in bytes. - */ -enum xp_retval -xpc_initiate_send(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size) -{ - struct xpc_partition *part = &xpc_partitions[partid]; - enum xp_retval ret = xpUnknownReason; - - dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload, - partid, ch_number); - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); - DBUG_ON(payload == NULL); - - if (xpc_part_ref(part)) { - ret = xpc_arch_ops.send_payload(&part->channels[ch_number], - flags, payload, payload_size, 0, NULL, NULL); - xpc_part_deref(part); - } - - return ret; -} - -/* - * Send a message that contains the user's payload on the specified channel - * connected to the specified partition. - * - * NOTE that this routine can sleep waiting for a message entry to become - * available. To not sleep, pass in the XPC_NOWAIT flag. - * - * This routine will not wait for the message to be sent or received. - * - * Once the remote end of the channel has received the message, the function - * passed as an argument to xpc_initiate_send_notify() will be called. This - * allows the sender to free up or re-use any buffers referenced by the - * message, but does NOT mean the message has been processed at the remote - * end by a receiver. - * - * If this routine returns an error, the caller's function will NOT be called. - * - * Arguments: - * - * partid - ID of partition to which the channel is connected. - * ch_number - channel # to send message on. - * flags - see xp.h for valid flags. - * payload - pointer to the payload which is to be sent. - * payload_size - size of the payload in bytes. - * func - function to call with asynchronous notification of message - * receipt. THIS FUNCTION MUST BE NON-BLOCKING. - * key - user-defined key to be passed to the function when it's called. - */ -enum xp_retval -xpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size, xpc_notify_func func, void *key) -{ - struct xpc_partition *part = &xpc_partitions[partid]; - enum xp_retval ret = xpUnknownReason; - - dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload, - partid, ch_number); - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); - DBUG_ON(payload == NULL); - DBUG_ON(func == NULL); - - if (xpc_part_ref(part)) { - ret = xpc_arch_ops.send_payload(&part->channels[ch_number], - flags, payload, payload_size, XPC_N_CALL, func, key); - xpc_part_deref(part); - } - return ret; -} - -/* - * Deliver a message's payload to its intended recipient. - */ -void -xpc_deliver_payload(struct xpc_channel *ch) -{ - void *payload; - - payload = xpc_arch_ops.get_deliverable_payload(ch); - if (payload != NULL) { - - /* - * This ref is taken to protect the payload itself from being - * freed before the user is finished with it, which the user - * indicates by calling xpc_initiate_received(). - */ - xpc_msgqueue_ref(ch); - - atomic_inc(&ch->kthreads_active); - - if (ch->func != NULL) { - dev_dbg(xpc_chan, "ch->func() called, payload=0x%p " - "partid=%d channel=%d\n", payload, ch->partid, - ch->number); - - /* deliver the message to its intended recipient */ - ch->func(xpMsgReceived, ch->partid, ch->number, payload, - ch->key); - - dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p " - "partid=%d channel=%d\n", payload, ch->partid, - ch->number); - } - - atomic_dec(&ch->kthreads_active); - } -} - -/* - * Acknowledge receipt of a delivered message's payload. - * - * This function, although called by users, does not call xpc_part_ref() to - * ensure that the partition infrastructure is in place. It relies on the - * fact that we called xpc_msgqueue_ref() in xpc_deliver_payload(). - * - * Arguments: - * - * partid - ID of partition to which the channel is connected. - * ch_number - channel # message received on. - * payload - pointer to the payload area allocated via - * xpc_initiate_send() or xpc_initiate_send_notify(). - */ -void -xpc_initiate_received(short partid, int ch_number, void *payload) -{ - struct xpc_partition *part = &xpc_partitions[partid]; - struct xpc_channel *ch; - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); - - ch = &part->channels[ch_number]; - xpc_arch_ops.received_payload(ch, payload); - - /* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload() */ - xpc_msgqueue_deref(ch); -} diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c deleted file mode 100644 index 15b6a8e35681..000000000000 --- a/drivers/misc/sgi-xp/xpc_main.c +++ /dev/null @@ -1,1309 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) support - standard version. - * - * XPC provides a message passing capability that crosses partition - * boundaries. This module is made up of two parts: - * - * partition This part detects the presence/absence of other - * partitions. It provides a heartbeat and monitors - * the heartbeats of other partitions. - * - * channel This part manages the channels and sends/receives - * messages across them to/from other partitions. - * - * There are a couple of additional functions residing in XP, which - * provide an interface to XPC for its users. - * - * - * Caveats: - * - * . Currently on sn2, we have no way to determine which nasid an IRQ - * came from. Thus, xpc_send_IRQ_sn2() does a remote amo write - * followed by an IPI. The amo indicates where data is to be pulled - * from, so after the IPI arrives, the remote partition checks the amo - * word. The IPI can actually arrive before the amo however, so other - * code must periodically check for this case. Also, remote amo - * operations do not reliably time out. Thus we do a remote PIO read - * solely to know whether the remote partition is down and whether we - * should stop sending IPIs to it. This remote PIO read operation is - * set up in a special nofault region so SAL knows to ignore (and - * cleanup) any errors due to the remote amo write, PIO read, and/or - * PIO write operations. - * - * If/when new hardware solves this IPI problem, we should abandon - * the current approach. - * - */ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/sysctl.h> -#include <linux/device.h> -#include <linux/delay.h> -#include <linux/reboot.h> -#include <linux/kdebug.h> -#include <linux/kthread.h> -#include "xpc.h" - -#ifdef CONFIG_X86_64 -#include <asm/traps.h> -#endif - -/* define two XPC debug device structures to be used with dev_dbg() et al */ - -static struct device_driver xpc_dbg_name = { - .name = "xpc" -}; - -static struct device xpc_part_dbg_subname = { - .init_name = "", /* set to "part" at xpc_init() time */ - .driver = &xpc_dbg_name -}; - -static struct device xpc_chan_dbg_subname = { - .init_name = "", /* set to "chan" at xpc_init() time */ - .driver = &xpc_dbg_name -}; - -struct device *xpc_part = &xpc_part_dbg_subname; -struct device *xpc_chan = &xpc_chan_dbg_subname; - -static int xpc_kdebug_ignore; - -/* systune related variables for /proc/sys directories */ - -static int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL; -static int xpc_hb_min_interval = 1; -static int xpc_hb_max_interval = 10; - -static int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_INTERVAL; -static int xpc_hb_check_min_interval = 10; -static int xpc_hb_check_max_interval = 120; - -int xpc_disengage_timelimit = XPC_DISENGAGE_DEFAULT_TIMELIMIT; -static int xpc_disengage_min_timelimit; /* = 0 */ -static int xpc_disengage_max_timelimit = 120; - -static const struct ctl_table xpc_sys_xpc_hb[] = { - { - .procname = "hb_interval", - .data = &xpc_hb_interval, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xpc_hb_min_interval, - .extra2 = &xpc_hb_max_interval}, - { - .procname = "hb_check_interval", - .data = &xpc_hb_check_interval, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xpc_hb_check_min_interval, - .extra2 = &xpc_hb_check_max_interval}, -}; -static const struct ctl_table xpc_sys_xpc[] = { - { - .procname = "disengage_timelimit", - .data = &xpc_disengage_timelimit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xpc_disengage_min_timelimit, - .extra2 = &xpc_disengage_max_timelimit}, -}; - -static struct ctl_table_header *xpc_sysctl; -static struct ctl_table_header *xpc_sysctl_hb; - -/* non-zero if any remote partition disengage was timed out */ -int xpc_disengage_timedout; - -/* #of activate IRQs received and not yet processed */ -int xpc_activate_IRQ_rcvd; -DEFINE_SPINLOCK(xpc_activate_IRQ_rcvd_lock); - -/* IRQ handler notifies this wait queue on receipt of an IRQ */ -DECLARE_WAIT_QUEUE_HEAD(xpc_activate_IRQ_wq); - -static unsigned long xpc_hb_check_timeout; -static struct timer_list xpc_hb_timer; - -/* notification that the xpc_hb_checker thread has exited */ -static DECLARE_COMPLETION(xpc_hb_checker_exited); - -/* notification that the xpc_discovery thread has exited */ -static DECLARE_COMPLETION(xpc_discovery_exited); - -static void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *); - -static int xpc_system_reboot(struct notifier_block *, unsigned long, void *); -static struct notifier_block xpc_reboot_notifier = { - .notifier_call = xpc_system_reboot, -}; - -static int xpc_system_die(struct notifier_block *, unsigned long, void *); -static struct notifier_block xpc_die_notifier = { - .notifier_call = xpc_system_die, -}; - -struct xpc_arch_operations xpc_arch_ops; - -/* - * Timer function to enforce the timelimit on the partition disengage. - */ -static void -xpc_timeout_partition_disengage(struct timer_list *t) -{ - struct xpc_partition *part = timer_container_of(part, t, - disengage_timer); - - DBUG_ON(time_is_after_jiffies(part->disengage_timeout)); - - xpc_partition_disengaged_from_timer(part); - - DBUG_ON(part->disengage_timeout != 0); - DBUG_ON(xpc_arch_ops.partition_engaged(XPC_PARTID(part))); -} - -/* - * Timer to produce the heartbeat. The timer structures function is - * already set when this is initially called. A tunable is used to - * specify when the next timeout should occur. - */ -static void -xpc_hb_beater(struct timer_list *unused) -{ - xpc_arch_ops.increment_heartbeat(); - - if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) - wake_up_interruptible(&xpc_activate_IRQ_wq); - - xpc_hb_timer.expires = jiffies + (xpc_hb_interval * HZ); - add_timer(&xpc_hb_timer); -} - -static void -xpc_start_hb_beater(void) -{ - xpc_arch_ops.heartbeat_init(); - timer_setup(&xpc_hb_timer, xpc_hb_beater, 0); - xpc_hb_beater(NULL); -} - -static void -xpc_stop_hb_beater(void) -{ - timer_delete_sync(&xpc_hb_timer); - xpc_arch_ops.heartbeat_exit(); -} - -/* - * At periodic intervals, scan through all active partitions and ensure - * their heartbeat is still active. If not, the partition is deactivated. - */ -static void -xpc_check_remote_hb(void) -{ - struct xpc_partition *part; - short partid; - enum xp_retval ret; - - for (partid = 0; partid < xp_max_npartitions; partid++) { - - if (xpc_exiting) - break; - - if (partid == xp_partition_id) - continue; - - part = &xpc_partitions[partid]; - - if (part->act_state == XPC_P_AS_INACTIVE || - part->act_state == XPC_P_AS_DEACTIVATING) { - continue; - } - - ret = xpc_arch_ops.get_remote_heartbeat(part); - if (ret != xpSuccess) - XPC_DEACTIVATE_PARTITION(part, ret); - } -} - -/* - * This thread is responsible for nearly all of the partition - * activation/deactivation. - */ -static int -xpc_hb_checker(void *ignore) -{ - int force_IRQ = 0; - - /* this thread was marked active by xpc_hb_init() */ - - set_cpus_allowed_ptr(current, cpumask_of(XPC_HB_CHECK_CPU)); - - /* set our heartbeating to other partitions into motion */ - xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ); - xpc_start_hb_beater(); - - while (!xpc_exiting) { - - dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have " - "been received\n", - (int)(xpc_hb_check_timeout - jiffies), - xpc_activate_IRQ_rcvd); - - /* checking of remote heartbeats is skewed by IRQ handling */ - if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) { - xpc_hb_check_timeout = jiffies + - (xpc_hb_check_interval * HZ); - - dev_dbg(xpc_part, "checking remote heartbeats\n"); - xpc_check_remote_hb(); - } - - /* check for outstanding IRQs */ - if (xpc_activate_IRQ_rcvd > 0 || force_IRQ != 0) { - force_IRQ = 0; - dev_dbg(xpc_part, "processing activate IRQs " - "received\n"); - xpc_arch_ops.process_activate_IRQ_rcvd(); - } - - /* wait for IRQ or timeout */ - (void)wait_event_interruptible(xpc_activate_IRQ_wq, - (time_is_before_eq_jiffies( - xpc_hb_check_timeout) || - xpc_activate_IRQ_rcvd > 0 || - xpc_exiting)); - } - - xpc_stop_hb_beater(); - - dev_dbg(xpc_part, "heartbeat checker is exiting\n"); - - /* mark this thread as having exited */ - complete(&xpc_hb_checker_exited); - return 0; -} - -/* - * This thread will attempt to discover other partitions to activate - * based on info provided by SAL. This new thread is short lived and - * will exit once discovery is complete. - */ -static int -xpc_initiate_discovery(void *ignore) -{ - xpc_discovery(); - - dev_dbg(xpc_part, "discovery thread is exiting\n"); - - /* mark this thread as having exited */ - complete(&xpc_discovery_exited); - return 0; -} - -/* - * The first kthread assigned to a newly activated partition is the one - * created by XPC HB with which it calls xpc_activating(). XPC hangs on to - * that kthread until the partition is brought down, at which time that kthread - * returns back to XPC HB. (The return of that kthread will signify to XPC HB - * that XPC has dismantled all communication infrastructure for the associated - * partition.) This kthread becomes the channel manager for that partition. - * - * Each active partition has a channel manager, who, besides connecting and - * disconnecting channels, will ensure that each of the partition's connected - * channels has the required number of assigned kthreads to get the work done. - */ -static void -xpc_channel_mgr(struct xpc_partition *part) -{ - while (part->act_state != XPC_P_AS_DEACTIVATING || - atomic_read(&part->nchannels_active) > 0 || - !xpc_partition_disengaged(part)) { - - xpc_process_sent_chctl_flags(part); - - /* - * Wait until we've been requested to activate kthreads or - * all of the channel's message queues have been torn down or - * a signal is pending. - * - * The channel_mgr_requests is set to 1 after being awakened, - * This is done to prevent the channel mgr from making one pass - * through the loop for each request, since he will - * be servicing all the requests in one pass. The reason it's - * set to 1 instead of 0 is so that other kthreads will know - * that the channel mgr is running and won't bother trying to - * wake him up. - */ - atomic_dec(&part->channel_mgr_requests); - (void)wait_event_interruptible(part->channel_mgr_wq, - (atomic_read(&part->channel_mgr_requests) > 0 || - part->chctl.all_flags != 0 || - (part->act_state == XPC_P_AS_DEACTIVATING && - atomic_read(&part->nchannels_active) == 0 && - xpc_partition_disengaged(part)))); - atomic_set(&part->channel_mgr_requests, 1); - } -} - -/* - * Guarantee that the kzalloc'd memory is cacheline aligned. - */ -void * -xpc_kzalloc_cacheline_aligned(size_t size, gfp_t flags, void **base) -{ - /* see if kzalloc will give us cachline aligned memory by default */ - *base = kzalloc(size, flags); - if (*base == NULL) - return NULL; - - if ((u64)*base == L1_CACHE_ALIGN((u64)*base)) - return *base; - - kfree(*base); - - /* nope, we'll have to do it ourselves */ - *base = kzalloc(size + L1_CACHE_BYTES, flags); - if (*base == NULL) - return NULL; - - return (void *)L1_CACHE_ALIGN((u64)*base); -} - -/* - * Setup the channel structures necessary to support XPartition Communication - * between the specified remote partition and the local one. - */ -static enum xp_retval -xpc_setup_ch_structures(struct xpc_partition *part) -{ - enum xp_retval ret; - int ch_number; - struct xpc_channel *ch; - short partid = XPC_PARTID(part); - - /* - * Allocate all of the channel structures as a contiguous chunk of - * memory. - */ - DBUG_ON(part->channels != NULL); - part->channels = kzalloc_objs(struct xpc_channel, XPC_MAX_NCHANNELS); - if (part->channels == NULL) { - dev_err(xpc_chan, "can't get memory for channels\n"); - return xpNoMemory; - } - - /* allocate the remote open and close args */ - - part->remote_openclose_args = - xpc_kzalloc_cacheline_aligned(XPC_OPENCLOSE_ARGS_SIZE, - GFP_KERNEL, &part-> - remote_openclose_args_base); - if (part->remote_openclose_args == NULL) { - dev_err(xpc_chan, "can't get memory for remote connect args\n"); - ret = xpNoMemory; - goto out_1; - } - - part->chctl.all_flags = 0; - spin_lock_init(&part->chctl_lock); - - atomic_set(&part->channel_mgr_requests, 1); - init_waitqueue_head(&part->channel_mgr_wq); - - part->nchannels = XPC_MAX_NCHANNELS; - - atomic_set(&part->nchannels_active, 0); - atomic_set(&part->nchannels_engaged, 0); - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch = &part->channels[ch_number]; - - ch->partid = partid; - ch->number = ch_number; - ch->flags = XPC_C_DISCONNECTED; - - atomic_set(&ch->kthreads_assigned, 0); - atomic_set(&ch->kthreads_idle, 0); - atomic_set(&ch->kthreads_active, 0); - - atomic_set(&ch->references, 0); - atomic_set(&ch->n_to_notify, 0); - - spin_lock_init(&ch->lock); - init_completion(&ch->wdisconnect_wait); - - atomic_set(&ch->n_on_msg_allocate_wq, 0); - init_waitqueue_head(&ch->msg_allocate_wq); - init_waitqueue_head(&ch->idle_wq); - } - - ret = xpc_arch_ops.setup_ch_structures(part); - if (ret != xpSuccess) - goto out_2; - - /* - * With the setting of the partition setup_state to XPC_P_SS_SETUP, - * we're declaring that this partition is ready to go. - */ - part->setup_state = XPC_P_SS_SETUP; - - return xpSuccess; - - /* setup of ch structures failed */ -out_2: - kfree(part->remote_openclose_args_base); - part->remote_openclose_args = NULL; -out_1: - kfree(part->channels); - part->channels = NULL; - return ret; -} - -/* - * Teardown the channel structures necessary to support XPartition Communication - * between the specified remote partition and the local one. - */ -static void -xpc_teardown_ch_structures(struct xpc_partition *part) -{ - DBUG_ON(atomic_read(&part->nchannels_engaged) != 0); - DBUG_ON(atomic_read(&part->nchannels_active) != 0); - - /* - * Make this partition inaccessible to local processes by marking it - * as no longer setup. Then wait before proceeding with the teardown - * until all existing references cease. - */ - DBUG_ON(part->setup_state != XPC_P_SS_SETUP); - part->setup_state = XPC_P_SS_WTEARDOWN; - - wait_event(part->teardown_wq, (atomic_read(&part->references) == 0)); - - /* now we can begin tearing down the infrastructure */ - - xpc_arch_ops.teardown_ch_structures(part); - - kfree(part->remote_openclose_args_base); - part->remote_openclose_args = NULL; - kfree(part->channels); - part->channels = NULL; - - part->setup_state = XPC_P_SS_TORNDOWN; -} - -/* - * When XPC HB determines that a partition has come up, it will create a new - * kthread and that kthread will call this function to attempt to set up the - * basic infrastructure used for Cross Partition Communication with the newly - * upped partition. - * - * The kthread that was created by XPC HB and which setup the XPC - * infrastructure will remain assigned to the partition becoming the channel - * manager for that partition until the partition is deactivating, at which - * time the kthread will teardown the XPC infrastructure and then exit. - */ -static int -xpc_activating(void *__partid) -{ - short partid = (u64)__partid; - struct xpc_partition *part = &xpc_partitions[partid]; - unsigned long irq_flags; - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - - spin_lock_irqsave(&part->act_lock, irq_flags); - - if (part->act_state == XPC_P_AS_DEACTIVATING) { - part->act_state = XPC_P_AS_INACTIVE; - spin_unlock_irqrestore(&part->act_lock, irq_flags); - part->remote_rp_pa = 0; - return 0; - } - - /* indicate the thread is activating */ - DBUG_ON(part->act_state != XPC_P_AS_ACTIVATION_REQ); - part->act_state = XPC_P_AS_ACTIVATING; - - XPC_SET_REASON(part, 0, 0); - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - dev_dbg(xpc_part, "activating partition %d\n", partid); - - xpc_arch_ops.allow_hb(partid); - - if (xpc_setup_ch_structures(part) == xpSuccess) { - (void)xpc_part_ref(part); /* this will always succeed */ - - if (xpc_arch_ops.make_first_contact(part) == xpSuccess) { - xpc_mark_partition_active(part); - xpc_channel_mgr(part); - /* won't return until partition is deactivating */ - } - - xpc_part_deref(part); - xpc_teardown_ch_structures(part); - } - - xpc_arch_ops.disallow_hb(partid); - xpc_mark_partition_inactive(part); - - if (part->reason == xpReactivating) { - /* interrupting ourselves results in activating partition */ - xpc_arch_ops.request_partition_reactivation(part); - } - - return 0; -} - -void -xpc_activate_partition(struct xpc_partition *part) -{ - short partid = XPC_PARTID(part); - unsigned long irq_flags; - struct task_struct *kthread; - - spin_lock_irqsave(&part->act_lock, irq_flags); - - DBUG_ON(part->act_state != XPC_P_AS_INACTIVE); - - part->act_state = XPC_P_AS_ACTIVATION_REQ; - XPC_SET_REASON(part, xpCloneKThread, __LINE__); - - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - kthread = kthread_run(xpc_activating, (void *)((u64)partid), "xpc%02d", - partid); - if (IS_ERR(kthread)) { - spin_lock_irqsave(&part->act_lock, irq_flags); - part->act_state = XPC_P_AS_INACTIVE; - XPC_SET_REASON(part, xpCloneKThreadFailed, __LINE__); - spin_unlock_irqrestore(&part->act_lock, irq_flags); - } -} - -void -xpc_activate_kthreads(struct xpc_channel *ch, int needed) -{ - int idle = atomic_read(&ch->kthreads_idle); - int assigned = atomic_read(&ch->kthreads_assigned); - int wakeup; - - DBUG_ON(needed <= 0); - - if (idle > 0) { - wakeup = (needed > idle) ? idle : needed; - needed -= wakeup; - - dev_dbg(xpc_chan, "wakeup %d idle kthreads, partid=%d, " - "channel=%d\n", wakeup, ch->partid, ch->number); - - /* only wakeup the requested number of kthreads */ - wake_up_nr(&ch->idle_wq, wakeup); - } - - if (needed <= 0) - return; - - if (needed + assigned > ch->kthreads_assigned_limit) { - needed = ch->kthreads_assigned_limit - assigned; - if (needed <= 0) - return; - } - - dev_dbg(xpc_chan, "create %d new kthreads, partid=%d, channel=%d\n", - needed, ch->partid, ch->number); - - xpc_create_kthreads(ch, needed, 0); -} - -/* - * This function is where XPC's kthreads wait for messages to deliver. - */ -static void -xpc_kthread_waitmsgs(struct xpc_partition *part, struct xpc_channel *ch) -{ - int (*n_of_deliverable_payloads) (struct xpc_channel *) = - xpc_arch_ops.n_of_deliverable_payloads; - - do { - /* deliver messages to their intended recipients */ - - while (n_of_deliverable_payloads(ch) > 0 && - !(ch->flags & XPC_C_DISCONNECTING)) { - xpc_deliver_payload(ch); - } - - if (atomic_inc_return(&ch->kthreads_idle) > - ch->kthreads_idle_limit) { - /* too many idle kthreads on this channel */ - atomic_dec(&ch->kthreads_idle); - break; - } - - dev_dbg(xpc_chan, "idle kthread calling " - "wait_event_interruptible_exclusive()\n"); - - (void)wait_event_interruptible_exclusive(ch->idle_wq, - (n_of_deliverable_payloads(ch) > 0 || - (ch->flags & XPC_C_DISCONNECTING))); - - atomic_dec(&ch->kthreads_idle); - - } while (!(ch->flags & XPC_C_DISCONNECTING)); -} - -static int -xpc_kthread_start(void *args) -{ - short partid = XPC_UNPACK_ARG1(args); - u16 ch_number = XPC_UNPACK_ARG2(args); - struct xpc_partition *part = &xpc_partitions[partid]; - struct xpc_channel *ch; - int n_needed; - unsigned long irq_flags; - int (*n_of_deliverable_payloads) (struct xpc_channel *) = - xpc_arch_ops.n_of_deliverable_payloads; - - dev_dbg(xpc_chan, "kthread starting, partid=%d, channel=%d\n", - partid, ch_number); - - ch = &part->channels[ch_number]; - - if (!(ch->flags & XPC_C_DISCONNECTING)) { - - /* let registerer know that connection has been established */ - - spin_lock_irqsave(&ch->lock, irq_flags); - if (!(ch->flags & XPC_C_CONNECTEDCALLOUT)) { - ch->flags |= XPC_C_CONNECTEDCALLOUT; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - xpc_connected_callout(ch); - - spin_lock_irqsave(&ch->lock, irq_flags); - ch->flags |= XPC_C_CONNECTEDCALLOUT_MADE; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - /* - * It is possible that while the callout was being - * made that the remote partition sent some messages. - * If that is the case, we may need to activate - * additional kthreads to help deliver them. We only - * need one less than total #of messages to deliver. - */ - n_needed = n_of_deliverable_payloads(ch) - 1; - if (n_needed > 0 && !(ch->flags & XPC_C_DISCONNECTING)) - xpc_activate_kthreads(ch, n_needed); - - } else { - spin_unlock_irqrestore(&ch->lock, irq_flags); - } - - xpc_kthread_waitmsgs(part, ch); - } - - /* let registerer know that connection is disconnecting */ - - spin_lock_irqsave(&ch->lock, irq_flags); - if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && - !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) { - ch->flags |= XPC_C_DISCONNECTINGCALLOUT; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - xpc_disconnect_callout(ch, xpDisconnecting); - - spin_lock_irqsave(&ch->lock, irq_flags); - ch->flags |= XPC_C_DISCONNECTINGCALLOUT_MADE; - } - spin_unlock_irqrestore(&ch->lock, irq_flags); - - if (atomic_dec_return(&ch->kthreads_assigned) == 0 && - atomic_dec_return(&part->nchannels_engaged) == 0) { - xpc_arch_ops.indicate_partition_disengaged(part); - } - - xpc_msgqueue_deref(ch); - - dev_dbg(xpc_chan, "kthread exiting, partid=%d, channel=%d\n", - partid, ch_number); - - xpc_part_deref(part); - return 0; -} - -/* - * For each partition that XPC has established communications with, there is - * a minimum of one kernel thread assigned to perform any operation that - * may potentially sleep or block (basically the callouts to the asynchronous - * functions registered via xpc_connect()). - * - * Additional kthreads are created and destroyed by XPC as the workload - * demands. - * - * A kthread is assigned to one of the active channels that exists for a given - * partition. - */ -void -xpc_create_kthreads(struct xpc_channel *ch, int needed, - int ignore_disconnecting) -{ - unsigned long irq_flags; - u64 args = XPC_PACK_ARGS(ch->partid, ch->number); - struct xpc_partition *part = &xpc_partitions[ch->partid]; - struct task_struct *kthread; - void (*indicate_partition_disengaged) (struct xpc_partition *) = - xpc_arch_ops.indicate_partition_disengaged; - - while (needed-- > 0) { - - /* - * The following is done on behalf of the newly created - * kthread. That kthread is responsible for doing the - * counterpart to the following before it exits. - */ - if (ignore_disconnecting) { - if (!atomic_inc_not_zero(&ch->kthreads_assigned)) { - /* kthreads assigned had gone to zero */ - BUG_ON(!(ch->flags & - XPC_C_DISCONNECTINGCALLOUT_MADE)); - break; - } - - } else if (ch->flags & XPC_C_DISCONNECTING) { - break; - - } else if (atomic_inc_return(&ch->kthreads_assigned) == 1 && - atomic_inc_return(&part->nchannels_engaged) == 1) { - xpc_arch_ops.indicate_partition_engaged(part); - } - (void)xpc_part_ref(part); - xpc_msgqueue_ref(ch); - - kthread = kthread_run(xpc_kthread_start, (void *)args, - "xpc%02dc%d", ch->partid, ch->number); - if (IS_ERR(kthread)) { - /* the fork failed */ - - /* - * NOTE: if (ignore_disconnecting && - * !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) is true, - * then we'll deadlock if all other kthreads assigned - * to this channel are blocked in the channel's - * registerer, because the only thing that will unblock - * them is the xpDisconnecting callout that this - * failed kthread_run() would have made. - */ - - if (atomic_dec_return(&ch->kthreads_assigned) == 0 && - atomic_dec_return(&part->nchannels_engaged) == 0) { - indicate_partition_disengaged(part); - } - xpc_msgqueue_deref(ch); - xpc_part_deref(part); - - if (atomic_read(&ch->kthreads_assigned) < - ch->kthreads_idle_limit) { - /* - * Flag this as an error only if we have an - * insufficient #of kthreads for the channel - * to function. - */ - spin_lock_irqsave(&ch->lock, irq_flags); - XPC_DISCONNECT_CHANNEL(ch, xpLackOfResources, - &irq_flags); - spin_unlock_irqrestore(&ch->lock, irq_flags); - } - break; - } - } -} - -void -xpc_disconnect_wait(int ch_number) -{ - unsigned long irq_flags; - short partid; - struct xpc_partition *part; - struct xpc_channel *ch; - int wakeup_channel_mgr; - - /* now wait for all callouts to the caller's function to cease */ - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (!xpc_part_ref(part)) - continue; - - ch = &part->channels[ch_number]; - - if (!(ch->flags & XPC_C_WDISCONNECT)) { - xpc_part_deref(part); - continue; - } - - wait_for_completion(&ch->wdisconnect_wait); - - spin_lock_irqsave(&ch->lock, irq_flags); - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); - wakeup_channel_mgr = 0; - - if (ch->delayed_chctl_flags) { - if (part->act_state != XPC_P_AS_DEACTIVATING) { - spin_lock(&part->chctl_lock); - part->chctl.flags[ch->number] |= - ch->delayed_chctl_flags; - spin_unlock(&part->chctl_lock); - wakeup_channel_mgr = 1; - } - ch->delayed_chctl_flags = 0; - } - - ch->flags &= ~XPC_C_WDISCONNECT; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - if (wakeup_channel_mgr) - xpc_wakeup_channel_mgr(part); - - xpc_part_deref(part); - } -} - -static int -xpc_setup_partitions(void) -{ - short partid; - struct xpc_partition *part; - - xpc_partitions = kzalloc_objs(struct xpc_partition, xp_max_npartitions); - if (xpc_partitions == NULL) { - dev_err(xpc_part, "can't get memory for partition structure\n"); - return -ENOMEM; - } - - /* - * The first few fields of each entry of xpc_partitions[] need to - * be initialized now so that calls to xpc_connect() and - * xpc_disconnect() can be made prior to the activation of any remote - * partition. NOTE THAT NONE OF THE OTHER FIELDS BELONGING TO THESE - * ENTRIES ARE MEANINGFUL UNTIL AFTER AN ENTRY'S CORRESPONDING - * PARTITION HAS BEEN ACTIVATED. - */ - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - DBUG_ON((u64)part != L1_CACHE_ALIGN((u64)part)); - - part->activate_IRQ_rcvd = 0; - spin_lock_init(&part->act_lock); - part->act_state = XPC_P_AS_INACTIVE; - XPC_SET_REASON(part, 0, 0); - - timer_setup(&part->disengage_timer, - xpc_timeout_partition_disengage, 0); - - part->setup_state = XPC_P_SS_UNSET; - init_waitqueue_head(&part->teardown_wq); - atomic_set(&part->references, 0); - } - - return xpc_arch_ops.setup_partitions(); -} - -static void -xpc_teardown_partitions(void) -{ - xpc_arch_ops.teardown_partitions(); - kfree(xpc_partitions); -} - -static void -xpc_do_exit(enum xp_retval reason) -{ - short partid; - int active_part_count, printed_waiting_msg = 0; - struct xpc_partition *part; - unsigned long printmsg_time, disengage_timeout = 0; - - /* a 'rmmod XPC' and a 'reboot' cannot both end up here together */ - DBUG_ON(xpc_exiting == 1); - - /* - * Let the heartbeat checker thread and the discovery thread - * (if one is running) know that they should exit. Also wake up - * the heartbeat checker thread in case it's sleeping. - */ - xpc_exiting = 1; - wake_up_interruptible(&xpc_activate_IRQ_wq); - - /* wait for the discovery thread to exit */ - wait_for_completion(&xpc_discovery_exited); - - /* wait for the heartbeat checker thread to exit */ - wait_for_completion(&xpc_hb_checker_exited); - - /* sleep for a 1/3 of a second or so */ - (void)msleep_interruptible(300); - - /* wait for all partitions to become inactive */ - - printmsg_time = jiffies + (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ); - xpc_disengage_timedout = 0; - - do { - active_part_count = 0; - - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_partition_disengaged(part) && - part->act_state == XPC_P_AS_INACTIVE) { - continue; - } - - active_part_count++; - - XPC_DEACTIVATE_PARTITION(part, reason); - - if (part->disengage_timeout > disengage_timeout) - disengage_timeout = part->disengage_timeout; - } - - if (xpc_arch_ops.any_partition_engaged()) { - if (time_is_before_jiffies(printmsg_time)) { - dev_info(xpc_part, "waiting for remote " - "partitions to deactivate, timeout in " - "%ld seconds\n", (disengage_timeout - - jiffies) / HZ); - printmsg_time = jiffies + - (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ); - printed_waiting_msg = 1; - } - - } else if (active_part_count > 0) { - if (printed_waiting_msg) { - dev_info(xpc_part, "waiting for local partition" - " to deactivate\n"); - printed_waiting_msg = 0; - } - - } else { - if (!xpc_disengage_timedout) { - dev_info(xpc_part, "all partitions have " - "deactivated\n"); - } - break; - } - - /* sleep for a 1/3 of a second or so */ - (void)msleep_interruptible(300); - - } while (1); - - DBUG_ON(xpc_arch_ops.any_partition_engaged()); - - xpc_teardown_rsvd_page(); - - if (reason == xpUnloading) { - (void)unregister_die_notifier(&xpc_die_notifier); - (void)unregister_reboot_notifier(&xpc_reboot_notifier); - } - - /* clear the interface to XPC's functions */ - xpc_clear_interface(); - - if (xpc_sysctl) - unregister_sysctl_table(xpc_sysctl); - if (xpc_sysctl_hb) - unregister_sysctl_table(xpc_sysctl_hb); - - xpc_teardown_partitions(); - - if (is_uv_system()) - xpc_exit_uv(); -} - -/* - * This function is called when the system is being rebooted. - */ -static int -xpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused) -{ - enum xp_retval reason; - - switch (event) { - case SYS_RESTART: - reason = xpSystemReboot; - break; - case SYS_HALT: - reason = xpSystemHalt; - break; - case SYS_POWER_OFF: - reason = xpSystemPoweroff; - break; - default: - reason = xpSystemGoingDown; - } - - xpc_do_exit(reason); - return NOTIFY_DONE; -} - -/* Used to only allow one cpu to complete disconnect */ -static unsigned int xpc_die_disconnecting; - -/* - * Notify other partitions to deactivate from us by first disengaging from all - * references to our memory. - */ -static void -xpc_die_deactivate(void) -{ - struct xpc_partition *part; - short partid; - int any_engaged; - long keep_waiting; - long wait_to_print; - - if (cmpxchg(&xpc_die_disconnecting, 0, 1)) - return; - - /* keep xpc_hb_checker thread from doing anything (just in case) */ - xpc_exiting = 1; - - xpc_arch_ops.disallow_all_hbs(); /*indicate we're deactivated */ - - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_arch_ops.partition_engaged(partid) || - part->act_state != XPC_P_AS_INACTIVE) { - xpc_arch_ops.request_partition_deactivation(part); - xpc_arch_ops.indicate_partition_disengaged(part); - } - } - - /* - * Though we requested that all other partitions deactivate from us, - * we only wait until they've all disengaged or we've reached the - * defined timelimit. - * - * Given that one iteration through the following while-loop takes - * approximately 200 microseconds, calculate the #of loops to take - * before bailing and the #of loops before printing a waiting message. - */ - keep_waiting = xpc_disengage_timelimit * 1000 * 5; - wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * 1000 * 5; - - while (1) { - any_engaged = xpc_arch_ops.any_partition_engaged(); - if (!any_engaged) { - dev_info(xpc_part, "all partitions have deactivated\n"); - break; - } - - if (!keep_waiting--) { - for (partid = 0; partid < xp_max_npartitions; - partid++) { - if (xpc_arch_ops.partition_engaged(partid)) { - dev_info(xpc_part, "deactivate from " - "remote partition %d timed " - "out\n", partid); - } - } - break; - } - - if (!wait_to_print--) { - dev_info(xpc_part, "waiting for remote partitions to " - "deactivate, timeout in %ld seconds\n", - keep_waiting / (1000 * 5)); - wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * - 1000 * 5; - } - - udelay(200); - } -} - -/* - * This function is called when the system is being restarted or halted due - * to some sort of system failure. If this is the case we need to notify the - * other partitions to disengage from all references to our memory. - * This function can also be called when our heartbeater could be offlined - * for a time. In this case we need to notify other partitions to not worry - * about the lack of a heartbeat. - */ -static int -xpc_system_die(struct notifier_block *nb, unsigned long event, void *_die_args) -{ - struct die_args *die_args = _die_args; - - switch (event) { - case DIE_TRAP: - if (die_args->trapnr == X86_TRAP_DF) - xpc_die_deactivate(); - - if (((die_args->trapnr == X86_TRAP_MF) || - (die_args->trapnr == X86_TRAP_XF)) && - !user_mode(die_args->regs)) - xpc_die_deactivate(); - - break; - case DIE_INT3: - case DIE_DEBUG: - break; - case DIE_OOPS: - case DIE_GPF: - default: - xpc_die_deactivate(); - } - - return NOTIFY_DONE; -} - -static int __init -xpc_init(void) -{ - int ret; - struct task_struct *kthread; - - dev_set_name(xpc_part, "part"); - dev_set_name(xpc_chan, "chan"); - - if (is_uv_system()) { - ret = xpc_init_uv(); - - } else { - ret = -ENODEV; - } - - if (ret != 0) - return ret; - - ret = xpc_setup_partitions(); - if (ret != 0) { - dev_err(xpc_part, "can't get memory for partition structure\n"); - goto out_1; - } - - xpc_sysctl = register_sysctl("xpc", xpc_sys_xpc); - xpc_sysctl_hb = register_sysctl("xpc/hb", xpc_sys_xpc_hb); - - /* - * Fill the partition reserved page with the information needed by - * other partitions to discover we are alive and establish initial - * communications. - */ - ret = xpc_setup_rsvd_page(); - if (ret != 0) { - dev_err(xpc_part, "can't setup our reserved page\n"); - goto out_2; - } - - /* add ourselves to the reboot_notifier_list */ - ret = register_reboot_notifier(&xpc_reboot_notifier); - if (ret != 0) - dev_warn(xpc_part, "can't register reboot notifier\n"); - - /* add ourselves to the die_notifier list */ - ret = register_die_notifier(&xpc_die_notifier); - if (ret != 0) - dev_warn(xpc_part, "can't register die notifier\n"); - - /* - * The real work-horse behind xpc. This processes incoming - * interrupts and monitors remote heartbeats. - */ - kthread = kthread_run(xpc_hb_checker, NULL, XPC_HB_CHECK_THREAD_NAME); - if (IS_ERR(kthread)) { - dev_err(xpc_part, "failed while forking hb check thread\n"); - ret = -EBUSY; - goto out_3; - } - - /* - * Startup a thread that will attempt to discover other partitions to - * activate based on info provided by SAL. This new thread is short - * lived and will exit once discovery is complete. - */ - kthread = kthread_run(xpc_initiate_discovery, NULL, - XPC_DISCOVERY_THREAD_NAME); - if (IS_ERR(kthread)) { - dev_err(xpc_part, "failed while forking discovery thread\n"); - - /* mark this new thread as a non-starter */ - complete(&xpc_discovery_exited); - - xpc_do_exit(xpUnloading); - return -EBUSY; - } - - /* set the interface to point at XPC's functions */ - xpc_set_interface(xpc_initiate_connect, xpc_initiate_disconnect, - xpc_initiate_send, xpc_initiate_send_notify, - xpc_initiate_received, xpc_initiate_partid_to_nasids); - - return 0; - - /* initialization was not successful */ -out_3: - xpc_teardown_rsvd_page(); - - (void)unregister_die_notifier(&xpc_die_notifier); - (void)unregister_reboot_notifier(&xpc_reboot_notifier); -out_2: - if (xpc_sysctl_hb) - unregister_sysctl_table(xpc_sysctl_hb); - if (xpc_sysctl) - unregister_sysctl_table(xpc_sysctl); - - xpc_teardown_partitions(); -out_1: - if (is_uv_system()) - xpc_exit_uv(); - return ret; -} - -module_init(xpc_init); - -static void __exit -xpc_exit(void) -{ - xpc_do_exit(xpUnloading); -} - -module_exit(xpc_exit); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_DESCRIPTION("Cross Partition Communication (XPC) support"); -MODULE_LICENSE("GPL"); - -module_param(xpc_hb_interval, int, 0); -MODULE_PARM_DESC(xpc_hb_interval, "Number of seconds between " - "heartbeat increments."); - -module_param(xpc_hb_check_interval, int, 0); -MODULE_PARM_DESC(xpc_hb_check_interval, "Number of seconds between " - "heartbeat checks."); - -module_param(xpc_disengage_timelimit, int, 0); -MODULE_PARM_DESC(xpc_disengage_timelimit, "Number of seconds to wait " - "for disengage to complete."); - -module_param(xpc_kdebug_ignore, int, 0); -MODULE_PARM_DESC(xpc_kdebug_ignore, "Should lack of heartbeat be ignored by " - "other partitions when dropping into kdebug."); diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c deleted file mode 100644 index d0467010558c..000000000000 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ /dev/null @@ -1,545 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) partition support. - * - * This is the part of XPC that detects the presence/absence of - * other partitions. It provides a heartbeat and monitors the - * heartbeats of other partitions. - * - */ - -#include <linux/device.h> -#include <linux/hardirq.h> -#include <linux/slab.h> -#include "xpc.h" -#include <asm/uv/uv_hub.h> - -/* XPC is exiting flag */ -int xpc_exiting; - -/* this partition's reserved page pointers */ -struct xpc_rsvd_page *xpc_rsvd_page; -static unsigned long *xpc_part_nasids; -unsigned long *xpc_mach_nasids; - -static int xpc_nasid_mask_nbytes; /* #of bytes in nasid mask */ -int xpc_nasid_mask_nlongs; /* #of longs in nasid mask */ - -struct xpc_partition *xpc_partitions; - -/* - * Guarantee that the kmalloc'd memory is cacheline aligned. - */ -void * -xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base) -{ - /* see if kmalloc will give us cachline aligned memory by default */ - *base = kmalloc(size, flags); - if (*base == NULL) - return NULL; - - if ((u64)*base == L1_CACHE_ALIGN((u64)*base)) - return *base; - - kfree(*base); - - /* nope, we'll have to do it ourselves */ - *base = kmalloc(size + L1_CACHE_BYTES, flags); - if (*base == NULL) - return NULL; - - return (void *)L1_CACHE_ALIGN((u64)*base); -} - -/* - * Given a nasid, get the physical address of the partition's reserved page - * for that nasid. This function returns 0 on any error. - */ -static unsigned long -xpc_get_rsvd_page_pa(int nasid) -{ - enum xp_retval ret; - u64 cookie = 0; - unsigned long rp_pa = nasid; /* seed with nasid */ - size_t len = 0; - size_t buf_len = 0; - void *buf = NULL; - void *buf_base = NULL; - enum xp_retval (*get_partition_rsvd_page_pa) - (void *, u64 *, unsigned long *, size_t *) = - xpc_arch_ops.get_partition_rsvd_page_pa; - - while (1) { - - /* !!! rp_pa will need to be _gpa on UV. - * ??? So do we save it into the architecture specific parts - * ??? of the xpc_partition structure? Do we rename this - * ??? function or have two versions? Rename rp_pa for UV to - * ??? rp_gpa? - */ - ret = get_partition_rsvd_page_pa(buf, &cookie, &rp_pa, &len); - - dev_dbg(xpc_part, "SAL returned with ret=%d, cookie=0x%016lx, " - "address=0x%016lx, len=0x%016lx\n", ret, - (unsigned long)cookie, rp_pa, len); - - if (ret != xpNeedMoreInfo) - break; - - if (len > buf_len) { - kfree(buf_base); - buf_len = L1_CACHE_ALIGN(len); - buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL, - &buf_base); - if (buf_base == NULL) { - dev_err(xpc_part, "unable to kmalloc " - "len=0x%016lx\n", buf_len); - ret = xpNoMemory; - break; - } - } - - ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len); - if (ret != xpSuccess) { - dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret); - break; - } - } - - kfree(buf_base); - - if (ret != xpSuccess) - rp_pa = 0; - - dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa); - return rp_pa; -} - -/* - * Fill the partition reserved page with the information needed by - * other partitions to discover we are alive and establish initial - * communications. - */ -int -xpc_setup_rsvd_page(void) -{ - int ret; - struct xpc_rsvd_page *rp; - unsigned long rp_pa; - unsigned long new_ts_jiffies; - - /* get the local reserved page's address */ - - preempt_disable(); - rp_pa = xpc_get_rsvd_page_pa(xp_cpu_to_nasid(smp_processor_id())); - preempt_enable(); - if (rp_pa == 0) { - dev_err(xpc_part, "SAL failed to locate the reserved page\n"); - return -ESRCH; - } - rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa)); - - if (rp->SAL_version < 3) { - /* SAL_versions < 3 had a SAL_partid defined as a u8 */ - rp->SAL_partid &= 0xff; - } - BUG_ON(rp->SAL_partid != xp_partition_id); - - if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) { - dev_err(xpc_part, "the reserved page's partid of %d is outside " - "supported range (< 0 || >= %d)\n", rp->SAL_partid, - xp_max_npartitions); - return -EINVAL; - } - - rp->version = XPC_RP_VERSION; - rp->max_npartitions = xp_max_npartitions; - - /* establish the actual sizes of the nasid masks */ - if (rp->SAL_version == 1) { - /* SAL_version 1 didn't set the nasids_size field */ - rp->SAL_nasids_size = 128; - } - xpc_nasid_mask_nbytes = rp->SAL_nasids_size; - xpc_nasid_mask_nlongs = BITS_TO_LONGS(rp->SAL_nasids_size * - BITS_PER_BYTE); - - /* setup the pointers to the various items in the reserved page */ - xpc_part_nasids = XPC_RP_PART_NASIDS(rp); - xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp); - - ret = xpc_arch_ops.setup_rsvd_page(rp); - if (ret != 0) - return ret; - - /* - * Set timestamp of when reserved page was setup by XPC. - * This signifies to the remote partition that our reserved - * page is initialized. - */ - new_ts_jiffies = jiffies; - if (new_ts_jiffies == 0 || new_ts_jiffies == rp->ts_jiffies) - new_ts_jiffies++; - rp->ts_jiffies = new_ts_jiffies; - - xpc_rsvd_page = rp; - return 0; -} - -void -xpc_teardown_rsvd_page(void) -{ - /* a zero timestamp indicates our rsvd page is not initialized */ - xpc_rsvd_page->ts_jiffies = 0; -} - -/* - * Get a copy of a portion of the remote partition's rsvd page. - * - * remote_rp points to a buffer that is cacheline aligned for BTE copies and - * is large enough to contain a copy of their reserved page header and - * part_nasids mask. - */ -enum xp_retval -xpc_get_remote_rp(int nasid, unsigned long *discovered_nasids, - struct xpc_rsvd_page *remote_rp, unsigned long *remote_rp_pa) -{ - int l; - enum xp_retval ret; - - /* get the reserved page's physical address */ - - *remote_rp_pa = xpc_get_rsvd_page_pa(nasid); - if (*remote_rp_pa == 0) - return xpNoRsvdPageAddr; - - /* pull over the reserved page header and part_nasids mask */ - ret = xp_remote_memcpy(xp_pa(remote_rp), *remote_rp_pa, - XPC_RP_HEADER_SIZE + xpc_nasid_mask_nbytes); - if (ret != xpSuccess) - return ret; - - if (discovered_nasids != NULL) { - unsigned long *remote_part_nasids = - XPC_RP_PART_NASIDS(remote_rp); - - for (l = 0; l < xpc_nasid_mask_nlongs; l++) - discovered_nasids[l] |= remote_part_nasids[l]; - } - - /* zero timestamp indicates the reserved page has not been setup */ - if (remote_rp->ts_jiffies == 0) - return xpRsvdPageNotSet; - - if (XPC_VERSION_MAJOR(remote_rp->version) != - XPC_VERSION_MAJOR(XPC_RP_VERSION)) { - return xpBadVersion; - } - - /* check that both remote and local partids are valid for each side */ - if (remote_rp->SAL_partid < 0 || - remote_rp->SAL_partid >= xp_max_npartitions || - remote_rp->max_npartitions <= xp_partition_id) { - return xpInvalidPartid; - } - - if (remote_rp->SAL_partid == xp_partition_id) - return xpLocalPartid; - - return xpSuccess; -} - -/* - * See if the other side has responded to a partition deactivate request - * from us. Though we requested the remote partition to deactivate with regard - * to us, we really only need to wait for the other side to disengage from us. - */ -static int __xpc_partition_disengaged(struct xpc_partition *part, - bool from_timer) -{ - short partid = XPC_PARTID(part); - int disengaged; - - disengaged = !xpc_arch_ops.partition_engaged(partid); - if (part->disengage_timeout) { - if (!disengaged) { - if (time_is_after_jiffies(part->disengage_timeout)) { - /* timelimit hasn't been reached yet */ - return 0; - } - - /* - * Other side hasn't responded to our deactivate - * request in a timely fashion, so assume it's dead. - */ - - dev_info(xpc_part, "deactivate request to remote " - "partition %d timed out\n", partid); - xpc_disengage_timedout = 1; - xpc_arch_ops.assume_partition_disengaged(partid); - disengaged = 1; - } - part->disengage_timeout = 0; - - /* Cancel the timer function if not called from it */ - if (!from_timer) - timer_delete_sync(&part->disengage_timer); - - DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING && - part->act_state != XPC_P_AS_INACTIVE); - if (part->act_state != XPC_P_AS_INACTIVE) - xpc_wakeup_channel_mgr(part); - - xpc_arch_ops.cancel_partition_deactivation_request(part); - } - return disengaged; -} - -int xpc_partition_disengaged(struct xpc_partition *part) -{ - return __xpc_partition_disengaged(part, false); -} - -int xpc_partition_disengaged_from_timer(struct xpc_partition *part) -{ - return __xpc_partition_disengaged(part, true); -} - -/* - * Mark specified partition as active. - */ -enum xp_retval -xpc_mark_partition_active(struct xpc_partition *part) -{ - unsigned long irq_flags; - enum xp_retval ret; - - dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part)); - - spin_lock_irqsave(&part->act_lock, irq_flags); - if (part->act_state == XPC_P_AS_ACTIVATING) { - part->act_state = XPC_P_AS_ACTIVE; - ret = xpSuccess; - } else { - DBUG_ON(part->reason == xpSuccess); - ret = part->reason; - } - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - return ret; -} - -/* - * Start the process of deactivating the specified partition. - */ -void -xpc_deactivate_partition(const int line, struct xpc_partition *part, - enum xp_retval reason) -{ - unsigned long irq_flags; - - spin_lock_irqsave(&part->act_lock, irq_flags); - - if (part->act_state == XPC_P_AS_INACTIVE) { - XPC_SET_REASON(part, reason, line); - spin_unlock_irqrestore(&part->act_lock, irq_flags); - if (reason == xpReactivating) { - /* we interrupt ourselves to reactivate partition */ - xpc_arch_ops.request_partition_reactivation(part); - } - return; - } - if (part->act_state == XPC_P_AS_DEACTIVATING) { - if ((part->reason == xpUnloading && reason != xpUnloading) || - reason == xpReactivating) { - XPC_SET_REASON(part, reason, line); - } - spin_unlock_irqrestore(&part->act_lock, irq_flags); - return; - } - - part->act_state = XPC_P_AS_DEACTIVATING; - XPC_SET_REASON(part, reason, line); - - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - /* ask remote partition to deactivate with regard to us */ - xpc_arch_ops.request_partition_deactivation(part); - - /* set a timelimit on the disengage phase of the deactivation request */ - part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ); - part->disengage_timer.expires = part->disengage_timeout; - add_timer(&part->disengage_timer); - - dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n", - XPC_PARTID(part), reason); - - xpc_partition_going_down(part, reason); -} - -/* - * Mark specified partition as inactive. - */ -void -xpc_mark_partition_inactive(struct xpc_partition *part) -{ - unsigned long irq_flags; - - dev_dbg(xpc_part, "setting partition %d to INACTIVE\n", - XPC_PARTID(part)); - - spin_lock_irqsave(&part->act_lock, irq_flags); - part->act_state = XPC_P_AS_INACTIVE; - spin_unlock_irqrestore(&part->act_lock, irq_flags); - part->remote_rp_pa = 0; -} - -/* - * SAL has provided a partition and machine mask. The partition mask - * contains a bit for each even nasid in our partition. The machine - * mask contains a bit for each even nasid in the entire machine. - * - * Using those two bit arrays, we can determine which nasids are - * known in the machine. Each should also have a reserved page - * initialized if they are available for partitioning. - */ -void -xpc_discovery(void) -{ - void *remote_rp_base; - struct xpc_rsvd_page *remote_rp; - unsigned long remote_rp_pa; - int region; - int region_size; - int max_regions; - int nasid; - unsigned long *discovered_nasids; - enum xp_retval ret; - - remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE + - xpc_nasid_mask_nbytes, - GFP_KERNEL, &remote_rp_base); - if (remote_rp == NULL) - return; - - discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long), - GFP_KERNEL); - if (discovered_nasids == NULL) { - kfree(remote_rp_base); - return; - } - - /* - * The term 'region' in this context refers to the minimum number of - * nodes that can comprise an access protection grouping. The access - * protection is in regards to memory, IOI and IPI. - */ - region_size = xp_region_size; - - if (is_uv_system()) - max_regions = 256; - else { - max_regions = 64; - - switch (region_size) { - case 128: - max_regions *= 2; - fallthrough; - case 64: - max_regions *= 2; - fallthrough; - case 32: - max_regions *= 2; - region_size = 16; - } - } - - for (region = 0; region < max_regions; region++) { - - if (xpc_exiting) - break; - - dev_dbg(xpc_part, "searching region %d\n", region); - - for (nasid = (region * region_size * 2); - nasid < ((region + 1) * region_size * 2); nasid += 2) { - - if (xpc_exiting) - break; - - dev_dbg(xpc_part, "checking nasid %d\n", nasid); - - if (test_bit(nasid / 2, xpc_part_nasids)) { - dev_dbg(xpc_part, "PROM indicates Nasid %d is " - "part of the local partition; skipping " - "region\n", nasid); - break; - } - - if (!(test_bit(nasid / 2, xpc_mach_nasids))) { - dev_dbg(xpc_part, "PROM indicates Nasid %d was " - "not on Numa-Link network at reset\n", - nasid); - continue; - } - - if (test_bit(nasid / 2, discovered_nasids)) { - dev_dbg(xpc_part, "Nasid %d is part of a " - "partition which was previously " - "discovered\n", nasid); - continue; - } - - /* pull over the rsvd page header & part_nasids mask */ - - ret = xpc_get_remote_rp(nasid, discovered_nasids, - remote_rp, &remote_rp_pa); - if (ret != xpSuccess) { - dev_dbg(xpc_part, "unable to get reserved page " - "from nasid %d, reason=%d\n", nasid, - ret); - - if (ret == xpLocalPartid) - break; - - continue; - } - - xpc_arch_ops.request_partition_activation(remote_rp, - remote_rp_pa, nasid); - } - } - - kfree(discovered_nasids); - kfree(remote_rp_base); -} - -/* - * Given a partid, get the nasids owned by that partition from the - * remote partition's reserved page. - */ -enum xp_retval -xpc_initiate_partid_to_nasids(short partid, void *nasid_mask) -{ - struct xpc_partition *part; - unsigned long part_nasid_pa; - - part = &xpc_partitions[partid]; - if (part->remote_rp_pa == 0) - return xpPartitionDown; - - memset(nasid_mask, 0, xpc_nasid_mask_nbytes); - - part_nasid_pa = (unsigned long)XPC_RP_PART_NASIDS(part->remote_rp_pa); - - return xp_remote_memcpy(xp_pa(nasid_mask), part_nasid_pa, - xpc_nasid_mask_nbytes); -} diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c deleted file mode 100644 index 79c2f00ed4d7..000000000000 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ /dev/null @@ -1,1727 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2008-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) uv-based functions. - * - * Architecture specific implementation of common functions. - * - */ - -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/cpu.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/numa.h> -#include <asm/uv/uv_hub.h> -#include <asm/uv/bios.h> -#include <asm/uv/uv_irq.h> -#include "../sgi-gru/gru.h" -#include "../sgi-gru/grukservices.h" -#include "xpc.h" - -static struct xpc_heartbeat_uv *xpc_heartbeat_uv; - -#define XPC_ACTIVATE_MSG_SIZE_UV (1 * GRU_CACHE_LINE_BYTES) -#define XPC_ACTIVATE_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \ - XPC_ACTIVATE_MSG_SIZE_UV) -#define XPC_ACTIVATE_IRQ_NAME "xpc_activate" - -#define XPC_NOTIFY_MSG_SIZE_UV (2 * GRU_CACHE_LINE_BYTES) -#define XPC_NOTIFY_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \ - XPC_NOTIFY_MSG_SIZE_UV) -#define XPC_NOTIFY_IRQ_NAME "xpc_notify" - -static int xpc_mq_node = NUMA_NO_NODE; - -static struct xpc_gru_mq_uv *xpc_activate_mq_uv; -static struct xpc_gru_mq_uv *xpc_notify_mq_uv; - -static int -xpc_setup_partitions_uv(void) -{ - short partid; - struct xpc_partition_uv *part_uv; - - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part_uv = &xpc_partitions[partid].sn.uv; - - mutex_init(&part_uv->cached_activate_gru_mq_desc_mutex); - spin_lock_init(&part_uv->flags_lock); - part_uv->remote_act_state = XPC_P_AS_INACTIVE; - } - return 0; -} - -static void -xpc_teardown_partitions_uv(void) -{ - short partid; - struct xpc_partition_uv *part_uv; - unsigned long irq_flags; - - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part_uv = &xpc_partitions[partid].sn.uv; - - if (part_uv->cached_activate_gru_mq_desc != NULL) { - mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex); - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - kfree(part_uv->cached_activate_gru_mq_desc); - part_uv->cached_activate_gru_mq_desc = NULL; - mutex_unlock(&part_uv-> - cached_activate_gru_mq_desc_mutex); - } - } -} - -static int -xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name) -{ - int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); - - mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset, - UV_AFFINITY_CPU); - if (mq->irq < 0) - return mq->irq; - - mq->mmr_value = uv_read_global_mmr64(mmr_pnode, mq->mmr_offset); - - return 0; -} - -static void -xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) -{ - uv_teardown_irq(mq->irq); -} - -static int -xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq) -{ - int ret; - - ret = uv_bios_mq_watchlist_alloc(uv_gpa(mq->address), - mq->order, &mq->mmr_offset); - if (ret < 0) { - dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, " - "ret=%d\n", ret); - return ret; - } - - mq->watchlist_num = ret; - return 0; -} - -static void -xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq) -{ - int ret; - int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); - - ret = uv_bios_mq_watchlist_free(mmr_pnode, mq->watchlist_num); - BUG_ON(ret != BIOS_STATUS_SUCCESS); -} - -static struct xpc_gru_mq_uv * -xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name, - irq_handler_t irq_handler) -{ - enum xp_retval xp_ret; - int ret; - int nid; - int nasid; - int pg_order; - struct page *page; - struct xpc_gru_mq_uv *mq; - struct uv_IO_APIC_route_entry *mmr_value; - - mq = kmalloc_obj(struct xpc_gru_mq_uv); - if (mq == NULL) { - dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() " - "a xpc_gru_mq_uv structure\n"); - ret = -ENOMEM; - goto out_0; - } - - mq->gru_mq_desc = kzalloc_obj(struct gru_message_queue_desc); - if (mq->gru_mq_desc == NULL) { - dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() " - "a gru_message_queue_desc structure\n"); - ret = -ENOMEM; - goto out_1; - } - - pg_order = get_order(mq_size); - mq->order = pg_order + PAGE_SHIFT; - mq_size = 1UL << mq->order; - - mq->mmr_blade = uv_cpu_to_blade_id(cpu); - - nid = cpu_to_node(cpu); - page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE, - pg_order); - if (page == NULL) { - dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d " - "bytes of memory on nid=%d for GRU mq\n", mq_size, nid); - ret = -ENOMEM; - goto out_2; - } - mq->address = page_address(page); - - /* enable generation of irq when GRU mq operation occurs to this mq */ - ret = xpc_gru_mq_watchlist_alloc_uv(mq); - if (ret != 0) - goto out_3; - - ret = xpc_get_gru_mq_irq_uv(mq, cpu, irq_name); - if (ret != 0) - goto out_4; - - ret = request_irq(mq->irq, irq_handler, 0, irq_name, NULL); - if (ret != 0) { - dev_err(xpc_part, "request_irq(irq=%d) returned error=%d\n", - mq->irq, -ret); - goto out_5; - } - - nasid = UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpu)); - - mmr_value = (struct uv_IO_APIC_route_entry *)&mq->mmr_value; - ret = gru_create_message_queue(mq->gru_mq_desc, mq->address, mq_size, - nasid, mmr_value->vector, mmr_value->dest); - if (ret != 0) { - dev_err(xpc_part, "gru_create_message_queue() returned " - "error=%d\n", ret); - ret = -EINVAL; - goto out_6; - } - - /* allow other partitions to access this GRU mq */ - xp_ret = xp_expand_memprotect(xp_pa(mq->address), mq_size); - if (xp_ret != xpSuccess) { - ret = -EACCES; - goto out_6; - } - - return mq; - - /* something went wrong */ -out_6: - free_irq(mq->irq, NULL); -out_5: - xpc_release_gru_mq_irq_uv(mq); -out_4: - xpc_gru_mq_watchlist_free_uv(mq); -out_3: - free_pages((unsigned long)mq->address, pg_order); -out_2: - kfree(mq->gru_mq_desc); -out_1: - kfree(mq); -out_0: - return ERR_PTR(ret); -} - -static void -xpc_destroy_gru_mq_uv(struct xpc_gru_mq_uv *mq) -{ - unsigned int mq_size; - int pg_order; - int ret; - - /* disallow other partitions to access GRU mq */ - mq_size = 1UL << mq->order; - ret = xp_restrict_memprotect(xp_pa(mq->address), mq_size); - BUG_ON(ret != xpSuccess); - - /* unregister irq handler and release mq irq/vector mapping */ - free_irq(mq->irq, NULL); - xpc_release_gru_mq_irq_uv(mq); - - /* disable generation of irq when GRU mq op occurs to this mq */ - xpc_gru_mq_watchlist_free_uv(mq); - - pg_order = mq->order - PAGE_SHIFT; - free_pages((unsigned long)mq->address, pg_order); - - kfree(mq); -} - -static enum xp_retval -xpc_send_gru_msg(struct gru_message_queue_desc *gru_mq_desc, void *msg, - size_t msg_size) -{ - enum xp_retval xp_ret; - int ret; - - while (1) { - ret = gru_send_message_gpa(gru_mq_desc, msg, msg_size); - if (ret == MQE_OK) { - xp_ret = xpSuccess; - break; - } - - if (ret == MQE_QUEUE_FULL) { - dev_dbg(xpc_chan, "gru_send_message_gpa() returned " - "error=MQE_QUEUE_FULL\n"); - /* !!! handle QLimit reached; delay & try again */ - /* ??? Do we add a limit to the number of retries? */ - (void)msleep_interruptible(10); - } else if (ret == MQE_CONGESTION) { - dev_dbg(xpc_chan, "gru_send_message_gpa() returned " - "error=MQE_CONGESTION\n"); - /* !!! handle LB Overflow; simply try again */ - /* ??? Do we add a limit to the number of retries? */ - } else { - /* !!! Currently this is MQE_UNEXPECTED_CB_ERR */ - dev_err(xpc_chan, "gru_send_message_gpa() returned " - "error=%d\n", ret); - xp_ret = xpGruSendMqError; - break; - } - } - return xp_ret; -} - -static void -xpc_process_activate_IRQ_rcvd_uv(void) -{ - unsigned long irq_flags; - short partid; - struct xpc_partition *part; - u8 act_state_req; - - DBUG_ON(xpc_activate_IRQ_rcvd == 0); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part = &xpc_partitions[partid]; - - if (part->sn.uv.act_state_req == 0) - continue; - - xpc_activate_IRQ_rcvd--; - BUG_ON(xpc_activate_IRQ_rcvd < 0); - - act_state_req = part->sn.uv.act_state_req; - part->sn.uv.act_state_req = 0; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - if (act_state_req == XPC_P_ASR_ACTIVATE_UV) { - if (part->act_state == XPC_P_AS_INACTIVE) - xpc_activate_partition(part); - else if (part->act_state == XPC_P_AS_DEACTIVATING) - XPC_DEACTIVATE_PARTITION(part, xpReactivating); - - } else if (act_state_req == XPC_P_ASR_REACTIVATE_UV) { - if (part->act_state == XPC_P_AS_INACTIVE) - xpc_activate_partition(part); - else - XPC_DEACTIVATE_PARTITION(part, xpReactivating); - - } else if (act_state_req == XPC_P_ASR_DEACTIVATE_UV) { - XPC_DEACTIVATE_PARTITION(part, part->sn.uv.reason); - - } else { - BUG(); - } - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (xpc_activate_IRQ_rcvd == 0) - break; - } - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - -} - -static void -xpc_handle_activate_mq_msg_uv(struct xpc_partition *part, - struct xpc_activate_mq_msghdr_uv *msg_hdr, - int part_setup, - int *wakeup_hb_checker) -{ - unsigned long irq_flags; - struct xpc_partition_uv *part_uv = &part->sn.uv; - struct xpc_openclose_args *args; - - part_uv->remote_act_state = msg_hdr->act_state; - - switch (msg_hdr->type) { - case XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV: - /* syncing of remote_act_state was just done above */ - break; - - case XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV: { - struct xpc_activate_mq_msg_activate_req_uv *msg; - - /* - * ??? Do we deal here with ts_jiffies being different - * ??? if act_state != XPC_P_AS_INACTIVE instead of - * ??? below? - */ - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_activate_req_uv, hdr); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_ACTIVATE_UV; - part->remote_rp_pa = msg->rp_gpa; /* !!! _pa is _gpa */ - part->remote_rp_ts_jiffies = msg_hdr->rp_ts_jiffies; - part_uv->heartbeat_gpa = msg->heartbeat_gpa; - - if (msg->activate_gru_mq_desc_gpa != - part_uv->activate_gru_mq_desc_gpa) { - spin_lock(&part_uv->flags_lock); - part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV; - spin_unlock(&part_uv->flags_lock); - part_uv->activate_gru_mq_desc_gpa = - msg->activate_gru_mq_desc_gpa; - } - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - break; - } - case XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV: { - struct xpc_activate_mq_msg_deactivate_req_uv *msg; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_deactivate_req_uv, hdr); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV; - part_uv->reason = msg->reason; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - return; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV: { - struct xpc_activate_mq_msg_chctl_closerequest_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_closerequest_uv, - hdr); - args = &part->remote_openclose_args[msg->ch_number]; - args->reason = msg->reason; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREQUEST; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV: { - struct xpc_activate_mq_msg_chctl_closereply_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_closereply_uv, - hdr); - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREPLY; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV: { - struct xpc_activate_mq_msg_chctl_openrequest_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_openrequest_uv, - hdr); - args = &part->remote_openclose_args[msg->ch_number]; - args->entry_size = msg->entry_size; - args->local_nentries = msg->local_nentries; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREQUEST; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV: { - struct xpc_activate_mq_msg_chctl_openreply_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_openreply_uv, hdr); - args = &part->remote_openclose_args[msg->ch_number]; - args->remote_nentries = msg->remote_nentries; - args->local_nentries = msg->local_nentries; - args->local_msgqueue_pa = msg->notify_gru_mq_desc_gpa; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREPLY; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV: { - struct xpc_activate_mq_msg_chctl_opencomplete_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_opencomplete_uv, hdr); - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENCOMPLETE; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - } - fallthrough; - case XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV: - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags |= XPC_P_ENGAGED_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - break; - - case XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV: - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags &= ~XPC_P_ENGAGED_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - break; - - default: - dev_err(xpc_part, "received unknown activate_mq msg type=%d " - "from partition=%d\n", msg_hdr->type, XPC_PARTID(part)); - - /* get hb checker to deactivate from the remote partition */ - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV; - part_uv->reason = xpBadMsgType; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - return; - } - - if (msg_hdr->rp_ts_jiffies != part->remote_rp_ts_jiffies && - part->remote_rp_ts_jiffies != 0) { - /* - * ??? Does what we do here need to be sensitive to - * ??? act_state or remote_act_state? - */ - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_REACTIVATE_UV; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - } -} - -static irqreturn_t -xpc_handle_activate_IRQ_uv(int irq, void *dev_id) -{ - struct xpc_activate_mq_msghdr_uv *msg_hdr; - short partid; - struct xpc_partition *part; - int wakeup_hb_checker = 0; - int part_referenced; - - while (1) { - msg_hdr = gru_get_next_message(xpc_activate_mq_uv->gru_mq_desc); - if (msg_hdr == NULL) - break; - - partid = msg_hdr->partid; - if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) { - dev_err(xpc_part, "xpc_handle_activate_IRQ_uv() " - "received invalid partid=0x%x in message\n", - partid); - } else { - part = &xpc_partitions[partid]; - - part_referenced = xpc_part_ref(part); - xpc_handle_activate_mq_msg_uv(part, msg_hdr, - part_referenced, - &wakeup_hb_checker); - if (part_referenced) - xpc_part_deref(part); - } - - gru_free_message(xpc_activate_mq_uv->gru_mq_desc, msg_hdr); - } - - if (wakeup_hb_checker) - wake_up_interruptible(&xpc_activate_IRQ_wq); - - return IRQ_HANDLED; -} - -static enum xp_retval -xpc_cache_remote_gru_mq_desc_uv(struct gru_message_queue_desc *gru_mq_desc, - unsigned long gru_mq_desc_gpa) -{ - enum xp_retval ret; - - ret = xp_remote_memcpy(uv_gpa(gru_mq_desc), gru_mq_desc_gpa, - sizeof(struct gru_message_queue_desc)); - if (ret == xpSuccess) - gru_mq_desc->mq = NULL; - - return ret; -} - -static enum xp_retval -xpc_send_activate_IRQ_uv(struct xpc_partition *part, void *msg, size_t msg_size, - int msg_type) -{ - struct xpc_activate_mq_msghdr_uv *msg_hdr = msg; - struct xpc_partition_uv *part_uv = &part->sn.uv; - struct gru_message_queue_desc *gru_mq_desc; - unsigned long irq_flags; - enum xp_retval ret; - - DBUG_ON(msg_size > XPC_ACTIVATE_MSG_SIZE_UV); - - msg_hdr->type = msg_type; - msg_hdr->partid = xp_partition_id; - msg_hdr->act_state = part->act_state; - msg_hdr->rp_ts_jiffies = xpc_rsvd_page->ts_jiffies; - - mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex); -again: - if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV)) { - gru_mq_desc = part_uv->cached_activate_gru_mq_desc; - if (gru_mq_desc == NULL) { - gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc, - GFP_ATOMIC); - if (gru_mq_desc == NULL) { - ret = xpNoMemory; - goto done; - } - part_uv->cached_activate_gru_mq_desc = gru_mq_desc; - } - - ret = xpc_cache_remote_gru_mq_desc_uv(gru_mq_desc, - part_uv-> - activate_gru_mq_desc_gpa); - if (ret != xpSuccess) - goto done; - - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags |= XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - } - - /* ??? Is holding a spin_lock (ch->lock) during this call a bad idea? */ - ret = xpc_send_gru_msg(part_uv->cached_activate_gru_mq_desc, msg, - msg_size); - if (ret != xpSuccess) { - smp_rmb(); /* ensure a fresh copy of part_uv->flags */ - if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV)) - goto again; - } -done: - mutex_unlock(&part_uv->cached_activate_gru_mq_desc_mutex); - return ret; -} - -static void -xpc_send_activate_IRQ_part_uv(struct xpc_partition *part, void *msg, - size_t msg_size, int msg_type) -{ - enum xp_retval ret; - - ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type); - if (unlikely(ret != xpSuccess)) - XPC_DEACTIVATE_PARTITION(part, ret); -} - -static void -xpc_send_activate_IRQ_ch_uv(struct xpc_channel *ch, unsigned long *irq_flags, - void *msg, size_t msg_size, int msg_type) -{ - struct xpc_partition *part = &xpc_partitions[ch->partid]; - enum xp_retval ret; - - ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type); - if (unlikely(ret != xpSuccess)) { - if (irq_flags != NULL) - spin_unlock_irqrestore(&ch->lock, *irq_flags); - - XPC_DEACTIVATE_PARTITION(part, ret); - - if (irq_flags != NULL) - spin_lock_irqsave(&ch->lock, *irq_flags); - } -} - -static void -xpc_send_local_activate_IRQ_uv(struct xpc_partition *part, int act_state_req) -{ - unsigned long irq_flags; - struct xpc_partition_uv *part_uv = &part->sn.uv; - - /* - * !!! Make our side think that the remote partition sent an activate - * !!! mq message our way by doing what the activate IRQ handler would - * !!! do had one really been sent. - */ - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = act_state_req; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - wake_up_interruptible(&xpc_activate_IRQ_wq); -} - -static enum xp_retval -xpc_get_partition_rsvd_page_pa_uv(void *buf, u64 *cookie, unsigned long *rp_pa, - size_t *len) -{ - s64 status; - enum xp_retval ret; - - status = uv_bios_reserved_page_pa((u64)buf, cookie, (u64 *)rp_pa, - (u64 *)len); - if (status == BIOS_STATUS_SUCCESS) - ret = xpSuccess; - else if (status == BIOS_STATUS_MORE_PASSES) - ret = xpNeedMoreInfo; - else - ret = xpBiosError; - - return ret; -} - -static int -xpc_setup_rsvd_page_uv(struct xpc_rsvd_page *rp) -{ - xpc_heartbeat_uv = - &xpc_partitions[sn_partition_id].sn.uv.cached_heartbeat; - rp->sn.uv.heartbeat_gpa = uv_gpa(xpc_heartbeat_uv); - rp->sn.uv.activate_gru_mq_desc_gpa = - uv_gpa(xpc_activate_mq_uv->gru_mq_desc); - return 0; -} - -static void -xpc_allow_hb_uv(short partid) -{ -} - -static void -xpc_disallow_hb_uv(short partid) -{ -} - -static void -xpc_disallow_all_hbs_uv(void) -{ -} - -static void -xpc_increment_heartbeat_uv(void) -{ - xpc_heartbeat_uv->value++; -} - -static void -xpc_offline_heartbeat_uv(void) -{ - xpc_increment_heartbeat_uv(); - xpc_heartbeat_uv->offline = 1; -} - -static void -xpc_online_heartbeat_uv(void) -{ - xpc_increment_heartbeat_uv(); - xpc_heartbeat_uv->offline = 0; -} - -static void -xpc_heartbeat_init_uv(void) -{ - xpc_heartbeat_uv->value = 1; - xpc_heartbeat_uv->offline = 0; -} - -static void -xpc_heartbeat_exit_uv(void) -{ - xpc_offline_heartbeat_uv(); -} - -static enum xp_retval -xpc_get_remote_heartbeat_uv(struct xpc_partition *part) -{ - struct xpc_partition_uv *part_uv = &part->sn.uv; - enum xp_retval ret; - - ret = xp_remote_memcpy(uv_gpa(&part_uv->cached_heartbeat), - part_uv->heartbeat_gpa, - sizeof(struct xpc_heartbeat_uv)); - if (ret != xpSuccess) - return ret; - - if (part_uv->cached_heartbeat.value == part->last_heartbeat && - !part_uv->cached_heartbeat.offline) { - - ret = xpNoHeartbeat; - } else { - part->last_heartbeat = part_uv->cached_heartbeat.value; - } - return ret; -} - -static void -xpc_request_partition_activation_uv(struct xpc_rsvd_page *remote_rp, - unsigned long remote_rp_gpa, int nasid) -{ - short partid = remote_rp->SAL_partid; - struct xpc_partition *part = &xpc_partitions[partid]; - struct xpc_activate_mq_msg_activate_req_uv msg; - - part->remote_rp_pa = remote_rp_gpa; /* !!! _pa here is really _gpa */ - part->remote_rp_ts_jiffies = remote_rp->ts_jiffies; - part->sn.uv.heartbeat_gpa = remote_rp->sn.uv.heartbeat_gpa; - part->sn.uv.activate_gru_mq_desc_gpa = - remote_rp->sn.uv.activate_gru_mq_desc_gpa; - - /* - * ??? Is it a good idea to make this conditional on what is - * ??? potentially stale state information? - */ - if (part->sn.uv.remote_act_state == XPC_P_AS_INACTIVE) { - msg.rp_gpa = uv_gpa(xpc_rsvd_page); - msg.heartbeat_gpa = xpc_rsvd_page->sn.uv.heartbeat_gpa; - msg.activate_gru_mq_desc_gpa = - xpc_rsvd_page->sn.uv.activate_gru_mq_desc_gpa; - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV); - } - - if (part->act_state == XPC_P_AS_INACTIVE) - xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV); -} - -static void -xpc_request_partition_reactivation_uv(struct xpc_partition *part) -{ - xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV); -} - -static void -xpc_request_partition_deactivation_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_deactivate_req_uv msg; - - /* - * ??? Is it a good idea to make this conditional on what is - * ??? potentially stale state information? - */ - if (part->sn.uv.remote_act_state != XPC_P_AS_DEACTIVATING && - part->sn.uv.remote_act_state != XPC_P_AS_INACTIVE) { - - msg.reason = part->reason; - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV); - } -} - -static void -xpc_cancel_partition_deactivation_request_uv(struct xpc_partition *part) -{ - /* nothing needs to be done */ - return; -} - -static void -xpc_init_fifo_uv(struct xpc_fifo_head_uv *head) -{ - head->first = NULL; - head->last = NULL; - spin_lock_init(&head->lock); - head->n_entries = 0; -} - -static void * -xpc_get_fifo_entry_uv(struct xpc_fifo_head_uv *head) -{ - unsigned long irq_flags; - struct xpc_fifo_entry_uv *first; - - spin_lock_irqsave(&head->lock, irq_flags); - first = head->first; - if (head->first != NULL) { - head->first = first->next; - if (head->first == NULL) - head->last = NULL; - - head->n_entries--; - BUG_ON(head->n_entries < 0); - - first->next = NULL; - } - spin_unlock_irqrestore(&head->lock, irq_flags); - return first; -} - -static void -xpc_put_fifo_entry_uv(struct xpc_fifo_head_uv *head, - struct xpc_fifo_entry_uv *last) -{ - unsigned long irq_flags; - - last->next = NULL; - spin_lock_irqsave(&head->lock, irq_flags); - if (head->last != NULL) - head->last->next = last; - else - head->first = last; - head->last = last; - head->n_entries++; - spin_unlock_irqrestore(&head->lock, irq_flags); -} - -static int -xpc_n_of_fifo_entries_uv(struct xpc_fifo_head_uv *head) -{ - return head->n_entries; -} - -/* - * Setup the channel structures that are uv specific. - */ -static enum xp_retval -xpc_setup_ch_structures_uv(struct xpc_partition *part) -{ - struct xpc_channel_uv *ch_uv; - int ch_number; - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch_uv = &part->channels[ch_number].sn.uv; - - xpc_init_fifo_uv(&ch_uv->msg_slot_free_list); - xpc_init_fifo_uv(&ch_uv->recv_msg_list); - } - - return xpSuccess; -} - -/* - * Teardown the channel structures that are uv specific. - */ -static void -xpc_teardown_ch_structures_uv(struct xpc_partition *part) -{ - /* nothing needs to be done */ - return; -} - -static enum xp_retval -xpc_make_first_contact_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_uv msg; - - /* - * We send a sync msg to get the remote partition's remote_act_state - * updated to our current act_state which at this point should - * be XPC_P_AS_ACTIVATING. - */ - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV); - - while (!((part->sn.uv.remote_act_state == XPC_P_AS_ACTIVATING) || - (part->sn.uv.remote_act_state == XPC_P_AS_ACTIVE))) { - - dev_dbg(xpc_part, "waiting to make first contact with " - "partition %d\n", XPC_PARTID(part)); - - /* wait a 1/4 of a second or so */ - (void)msleep_interruptible(250); - - if (part->act_state == XPC_P_AS_DEACTIVATING) - return part->reason; - } - - return xpSuccess; -} - -static u64 -xpc_get_chctl_all_flags_uv(struct xpc_partition *part) -{ - unsigned long irq_flags; - union xpc_channel_ctl_flags chctl; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - chctl = part->chctl; - if (chctl.all_flags != 0) - part->chctl.all_flags = 0; - - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - return chctl.all_flags; -} - -static enum xp_retval -xpc_allocate_send_msg_slot_uv(struct xpc_channel *ch) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - struct xpc_send_msg_slot_uv *msg_slot; - unsigned long irq_flags; - int nentries; - int entry; - size_t nbytes; - - for (nentries = ch->local_nentries; nentries > 0; nentries--) { - nbytes = nentries * sizeof(struct xpc_send_msg_slot_uv); - ch_uv->send_msg_slots = kzalloc(nbytes, GFP_KERNEL); - if (ch_uv->send_msg_slots == NULL) - continue; - - for (entry = 0; entry < nentries; entry++) { - msg_slot = &ch_uv->send_msg_slots[entry]; - - msg_slot->msg_slot_number = entry; - xpc_put_fifo_entry_uv(&ch_uv->msg_slot_free_list, - &msg_slot->next); - } - - spin_lock_irqsave(&ch->lock, irq_flags); - if (nentries < ch->local_nentries) - ch->local_nentries = nentries; - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpSuccess; - } - - return xpNoMemory; -} - -static enum xp_retval -xpc_allocate_recv_msg_slot_uv(struct xpc_channel *ch) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - struct xpc_notify_mq_msg_uv *msg_slot; - unsigned long irq_flags; - int nentries; - int entry; - size_t nbytes; - - for (nentries = ch->remote_nentries; nentries > 0; nentries--) { - nbytes = nentries * ch->entry_size; - ch_uv->recv_msg_slots = kzalloc(nbytes, GFP_KERNEL); - if (ch_uv->recv_msg_slots == NULL) - continue; - - for (entry = 0; entry < nentries; entry++) { - msg_slot = ch_uv->recv_msg_slots + - entry * ch->entry_size; - - msg_slot->hdr.msg_slot_number = entry; - } - - spin_lock_irqsave(&ch->lock, irq_flags); - if (nentries < ch->remote_nentries) - ch->remote_nentries = nentries; - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpSuccess; - } - - return xpNoMemory; -} - -/* - * Allocate msg_slots associated with the channel. - */ -static enum xp_retval -xpc_setup_msg_structures_uv(struct xpc_channel *ch) -{ - static enum xp_retval ret; - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - - DBUG_ON(ch->flags & XPC_C_SETUP); - - ch_uv->cached_notify_gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc); - if (ch_uv->cached_notify_gru_mq_desc == NULL) - return xpNoMemory; - - ret = xpc_allocate_send_msg_slot_uv(ch); - if (ret == xpSuccess) { - - ret = xpc_allocate_recv_msg_slot_uv(ch); - if (ret != xpSuccess) { - kfree(ch_uv->send_msg_slots); - xpc_init_fifo_uv(&ch_uv->msg_slot_free_list); - } - } - return ret; -} - -/* - * Free up msg_slots and clear other stuff that were setup for the specified - * channel. - */ -static void -xpc_teardown_msg_structures_uv(struct xpc_channel *ch) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - - lockdep_assert_held(&ch->lock); - - kfree(ch_uv->cached_notify_gru_mq_desc); - ch_uv->cached_notify_gru_mq_desc = NULL; - - if (ch->flags & XPC_C_SETUP) { - xpc_init_fifo_uv(&ch_uv->msg_slot_free_list); - kfree(ch_uv->send_msg_slots); - xpc_init_fifo_uv(&ch_uv->recv_msg_list); - kfree(ch_uv->recv_msg_slots); - } -} - -static void -xpc_send_chctl_closerequest_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_closerequest_uv msg; - - msg.ch_number = ch->number; - msg.reason = ch->reason; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV); -} - -static void -xpc_send_chctl_closereply_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_closereply_uv msg; - - msg.ch_number = ch->number; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV); -} - -static void -xpc_send_chctl_openrequest_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_openrequest_uv msg; - - msg.ch_number = ch->number; - msg.entry_size = ch->entry_size; - msg.local_nentries = ch->local_nentries; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV); -} - -static void -xpc_send_chctl_openreply_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_openreply_uv msg; - - msg.ch_number = ch->number; - msg.local_nentries = ch->local_nentries; - msg.remote_nentries = ch->remote_nentries; - msg.notify_gru_mq_desc_gpa = uv_gpa(xpc_notify_mq_uv->gru_mq_desc); - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV); -} - -static void -xpc_send_chctl_opencomplete_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_opencomplete_uv msg; - - msg.ch_number = ch->number; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV); -} - -static void -xpc_send_chctl_local_msgrequest_uv(struct xpc_partition *part, int ch_number) -{ - unsigned long irq_flags; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[ch_number] |= XPC_CHCTL_MSGREQUEST; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); -} - -static enum xp_retval -xpc_save_remote_msgqueue_pa_uv(struct xpc_channel *ch, - unsigned long gru_mq_desc_gpa) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - - DBUG_ON(ch_uv->cached_notify_gru_mq_desc == NULL); - return xpc_cache_remote_gru_mq_desc_uv(ch_uv->cached_notify_gru_mq_desc, - gru_mq_desc_gpa); -} - -static void -xpc_indicate_partition_engaged_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_uv msg; - - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV); -} - -static void -xpc_indicate_partition_disengaged_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_uv msg; - - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV); -} - -static void -xpc_assume_partition_disengaged_uv(short partid) -{ - struct xpc_partition_uv *part_uv = &xpc_partitions[partid].sn.uv; - unsigned long irq_flags; - - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags &= ~XPC_P_ENGAGED_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); -} - -static int -xpc_partition_engaged_uv(short partid) -{ - return (xpc_partitions[partid].sn.uv.flags & XPC_P_ENGAGED_UV) != 0; -} - -static int -xpc_any_partition_engaged_uv(void) -{ - struct xpc_partition_uv *part_uv; - short partid; - - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part_uv = &xpc_partitions[partid].sn.uv; - if ((part_uv->flags & XPC_P_ENGAGED_UV) != 0) - return 1; - } - return 0; -} - -static enum xp_retval -xpc_allocate_msg_slot_uv(struct xpc_channel *ch, u32 flags, - struct xpc_send_msg_slot_uv **address_of_msg_slot) -{ - enum xp_retval ret; - struct xpc_send_msg_slot_uv *msg_slot; - struct xpc_fifo_entry_uv *entry; - - while (1) { - entry = xpc_get_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list); - if (entry != NULL) - break; - - if (flags & XPC_NOWAIT) - return xpNoWait; - - ret = xpc_allocate_msg_wait(ch); - if (ret != xpInterrupted && ret != xpTimeout) - return ret; - } - - msg_slot = container_of(entry, struct xpc_send_msg_slot_uv, next); - *address_of_msg_slot = msg_slot; - return xpSuccess; -} - -static void -xpc_free_msg_slot_uv(struct xpc_channel *ch, - struct xpc_send_msg_slot_uv *msg_slot) -{ - xpc_put_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list, &msg_slot->next); - - /* wakeup anyone waiting for a free msg slot */ - if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) - wake_up(&ch->msg_allocate_wq); -} - -static void -xpc_notify_sender_uv(struct xpc_channel *ch, - struct xpc_send_msg_slot_uv *msg_slot, - enum xp_retval reason) -{ - xpc_notify_func func = msg_slot->func; - - if (func != NULL && cmpxchg(&msg_slot->func, func, NULL) == func) { - - atomic_dec(&ch->n_to_notify); - - dev_dbg(xpc_chan, "msg_slot->func() called, msg_slot=0x%p " - "msg_slot_number=%d partid=%d channel=%d\n", msg_slot, - msg_slot->msg_slot_number, ch->partid, ch->number); - - func(reason, ch->partid, ch->number, msg_slot->key); - - dev_dbg(xpc_chan, "msg_slot->func() returned, msg_slot=0x%p " - "msg_slot_number=%d partid=%d channel=%d\n", msg_slot, - msg_slot->msg_slot_number, ch->partid, ch->number); - } -} - -static void -xpc_handle_notify_mq_ack_uv(struct xpc_channel *ch, - struct xpc_notify_mq_msg_uv *msg) -{ - struct xpc_send_msg_slot_uv *msg_slot; - int entry = msg->hdr.msg_slot_number % ch->local_nentries; - - msg_slot = &ch->sn.uv.send_msg_slots[entry]; - - BUG_ON(msg_slot->msg_slot_number != msg->hdr.msg_slot_number); - msg_slot->msg_slot_number += ch->local_nentries; - - if (msg_slot->func != NULL) - xpc_notify_sender_uv(ch, msg_slot, xpMsgDelivered); - - xpc_free_msg_slot_uv(ch, msg_slot); -} - -static void -xpc_handle_notify_mq_msg_uv(struct xpc_partition *part, - struct xpc_notify_mq_msg_uv *msg) -{ - struct xpc_partition_uv *part_uv = &part->sn.uv; - struct xpc_channel *ch; - struct xpc_channel_uv *ch_uv; - struct xpc_notify_mq_msg_uv *msg_slot; - unsigned long irq_flags; - int ch_number = msg->hdr.ch_number; - - if (unlikely(ch_number >= part->nchannels)) { - dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received invalid " - "channel number=0x%x in message from partid=%d\n", - ch_number, XPC_PARTID(part)); - - /* get hb checker to deactivate from the remote partition */ - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV; - part_uv->reason = xpBadChannelNumber; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - wake_up_interruptible(&xpc_activate_IRQ_wq); - return; - } - - ch = &part->channels[ch_number]; - xpc_msgqueue_ref(ch); - - if (!(ch->flags & XPC_C_CONNECTED)) { - xpc_msgqueue_deref(ch); - return; - } - - /* see if we're really dealing with an ACK for a previously sent msg */ - if (msg->hdr.size == 0) { - xpc_handle_notify_mq_ack_uv(ch, msg); - xpc_msgqueue_deref(ch); - return; - } - - /* we're dealing with a normal message sent via the notify_mq */ - ch_uv = &ch->sn.uv; - - msg_slot = ch_uv->recv_msg_slots + - (msg->hdr.msg_slot_number % ch->remote_nentries) * ch->entry_size; - - BUG_ON(msg_slot->hdr.size != 0); - - memcpy(msg_slot, msg, msg->hdr.size); - - xpc_put_fifo_entry_uv(&ch_uv->recv_msg_list, &msg_slot->hdr.u.next); - - if (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) { - /* - * If there is an existing idle kthread get it to deliver - * the payload, otherwise we'll have to get the channel mgr - * for this partition to create a kthread to do the delivery. - */ - if (atomic_read(&ch->kthreads_idle) > 0) - wake_up_nr(&ch->idle_wq, 1); - else - xpc_send_chctl_local_msgrequest_uv(part, ch->number); - } - xpc_msgqueue_deref(ch); -} - -static irqreturn_t -xpc_handle_notify_IRQ_uv(int irq, void *dev_id) -{ - struct xpc_notify_mq_msg_uv *msg; - short partid; - struct xpc_partition *part; - - while ((msg = gru_get_next_message(xpc_notify_mq_uv->gru_mq_desc)) != - NULL) { - - partid = msg->hdr.partid; - if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) { - dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received " - "invalid partid=0x%x in message\n", partid); - } else { - part = &xpc_partitions[partid]; - - if (xpc_part_ref(part)) { - xpc_handle_notify_mq_msg_uv(part, msg); - xpc_part_deref(part); - } - } - - gru_free_message(xpc_notify_mq_uv->gru_mq_desc, msg); - } - - return IRQ_HANDLED; -} - -static int -xpc_n_of_deliverable_payloads_uv(struct xpc_channel *ch) -{ - return xpc_n_of_fifo_entries_uv(&ch->sn.uv.recv_msg_list); -} - -static void -xpc_process_msg_chctl_flags_uv(struct xpc_partition *part, int ch_number) -{ - struct xpc_channel *ch = &part->channels[ch_number]; - int ndeliverable_payloads; - - xpc_msgqueue_ref(ch); - - ndeliverable_payloads = xpc_n_of_deliverable_payloads_uv(ch); - - if (ndeliverable_payloads > 0 && - (ch->flags & XPC_C_CONNECTED) && - (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE)) { - - xpc_activate_kthreads(ch, ndeliverable_payloads); - } - - xpc_msgqueue_deref(ch); -} - -static enum xp_retval -xpc_send_payload_uv(struct xpc_channel *ch, u32 flags, void *payload, - u16 payload_size, u8 notify_type, xpc_notify_func func, - void *key) -{ - enum xp_retval ret = xpSuccess; - struct xpc_send_msg_slot_uv *msg_slot = NULL; - struct xpc_notify_mq_msg_uv *msg; - u8 msg_buffer[XPC_NOTIFY_MSG_SIZE_UV]; - size_t msg_size; - - DBUG_ON(notify_type != XPC_N_CALL); - - msg_size = sizeof(struct xpc_notify_mq_msghdr_uv) + payload_size; - if (msg_size > ch->entry_size) - return xpPayloadTooBig; - - xpc_msgqueue_ref(ch); - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - goto out_1; - } - if (!(ch->flags & XPC_C_CONNECTED)) { - ret = xpNotConnected; - goto out_1; - } - - ret = xpc_allocate_msg_slot_uv(ch, flags, &msg_slot); - if (ret != xpSuccess) - goto out_1; - - if (func != NULL) { - atomic_inc(&ch->n_to_notify); - - msg_slot->key = key; - smp_wmb(); /* a non-NULL func must hit memory after the key */ - msg_slot->func = func; - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - goto out_2; - } - } - - msg = (struct xpc_notify_mq_msg_uv *)&msg_buffer; - msg->hdr.partid = xp_partition_id; - msg->hdr.ch_number = ch->number; - msg->hdr.size = msg_size; - msg->hdr.msg_slot_number = msg_slot->msg_slot_number; - memcpy(&msg->payload, payload, payload_size); - - ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg, - msg_size); - if (ret == xpSuccess) - goto out_1; - - XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret); -out_2: - if (func != NULL) { - /* - * Try to NULL the msg_slot's func field. If we fail, then - * xpc_notify_senders_of_disconnect_uv() beat us to it, in which - * case we need to pretend we succeeded to send the message - * since the user will get a callout for the disconnect error - * by xpc_notify_senders_of_disconnect_uv(), and to also get an - * error returned here will confuse them. Additionally, since - * in this case the channel is being disconnected we don't need - * to put the msg_slot back on the free list. - */ - if (cmpxchg(&msg_slot->func, func, NULL) != func) { - ret = xpSuccess; - goto out_1; - } - - msg_slot->key = NULL; - atomic_dec(&ch->n_to_notify); - } - xpc_free_msg_slot_uv(ch, msg_slot); -out_1: - xpc_msgqueue_deref(ch); - return ret; -} - -/* - * Tell the callers of xpc_send_notify() that the status of their payloads - * is unknown because the channel is now disconnecting. - * - * We don't worry about putting these msg_slots on the free list since the - * msg_slots themselves are about to be kfree'd. - */ -static void -xpc_notify_senders_of_disconnect_uv(struct xpc_channel *ch) -{ - struct xpc_send_msg_slot_uv *msg_slot; - int entry; - - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING)); - - for (entry = 0; entry < ch->local_nentries; entry++) { - - if (atomic_read(&ch->n_to_notify) == 0) - break; - - msg_slot = &ch->sn.uv.send_msg_slots[entry]; - if (msg_slot->func != NULL) - xpc_notify_sender_uv(ch, msg_slot, ch->reason); - } -} - -/* - * Get the next deliverable message's payload. - */ -static void * -xpc_get_deliverable_payload_uv(struct xpc_channel *ch) -{ - struct xpc_fifo_entry_uv *entry; - struct xpc_notify_mq_msg_uv *msg; - void *payload = NULL; - - if (!(ch->flags & XPC_C_DISCONNECTING)) { - entry = xpc_get_fifo_entry_uv(&ch->sn.uv.recv_msg_list); - if (entry != NULL) { - msg = container_of(entry, struct xpc_notify_mq_msg_uv, - hdr.u.next); - payload = &msg->payload; - } - } - return payload; -} - -static void -xpc_received_payload_uv(struct xpc_channel *ch, void *payload) -{ - struct xpc_notify_mq_msg_uv *msg; - enum xp_retval ret; - - msg = container_of(payload, struct xpc_notify_mq_msg_uv, payload); - - /* return an ACK to the sender of this message */ - - msg->hdr.partid = xp_partition_id; - msg->hdr.size = 0; /* size of zero indicates this is an ACK */ - - ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg, - sizeof(struct xpc_notify_mq_msghdr_uv)); - if (ret != xpSuccess) - XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret); -} - -static const struct xpc_arch_operations xpc_arch_ops_uv = { - .setup_partitions = xpc_setup_partitions_uv, - .teardown_partitions = xpc_teardown_partitions_uv, - .process_activate_IRQ_rcvd = xpc_process_activate_IRQ_rcvd_uv, - .get_partition_rsvd_page_pa = xpc_get_partition_rsvd_page_pa_uv, - .setup_rsvd_page = xpc_setup_rsvd_page_uv, - - .allow_hb = xpc_allow_hb_uv, - .disallow_hb = xpc_disallow_hb_uv, - .disallow_all_hbs = xpc_disallow_all_hbs_uv, - .increment_heartbeat = xpc_increment_heartbeat_uv, - .offline_heartbeat = xpc_offline_heartbeat_uv, - .online_heartbeat = xpc_online_heartbeat_uv, - .heartbeat_init = xpc_heartbeat_init_uv, - .heartbeat_exit = xpc_heartbeat_exit_uv, - .get_remote_heartbeat = xpc_get_remote_heartbeat_uv, - - .request_partition_activation = - xpc_request_partition_activation_uv, - .request_partition_reactivation = - xpc_request_partition_reactivation_uv, - .request_partition_deactivation = - xpc_request_partition_deactivation_uv, - .cancel_partition_deactivation_request = - xpc_cancel_partition_deactivation_request_uv, - - .setup_ch_structures = xpc_setup_ch_structures_uv, - .teardown_ch_structures = xpc_teardown_ch_structures_uv, - - .make_first_contact = xpc_make_first_contact_uv, - - .get_chctl_all_flags = xpc_get_chctl_all_flags_uv, - .send_chctl_closerequest = xpc_send_chctl_closerequest_uv, - .send_chctl_closereply = xpc_send_chctl_closereply_uv, - .send_chctl_openrequest = xpc_send_chctl_openrequest_uv, - .send_chctl_openreply = xpc_send_chctl_openreply_uv, - .send_chctl_opencomplete = xpc_send_chctl_opencomplete_uv, - .process_msg_chctl_flags = xpc_process_msg_chctl_flags_uv, - - .save_remote_msgqueue_pa = xpc_save_remote_msgqueue_pa_uv, - - .setup_msg_structures = xpc_setup_msg_structures_uv, - .teardown_msg_structures = xpc_teardown_msg_structures_uv, - - .indicate_partition_engaged = xpc_indicate_partition_engaged_uv, - .indicate_partition_disengaged = xpc_indicate_partition_disengaged_uv, - .assume_partition_disengaged = xpc_assume_partition_disengaged_uv, - .partition_engaged = xpc_partition_engaged_uv, - .any_partition_engaged = xpc_any_partition_engaged_uv, - - .n_of_deliverable_payloads = xpc_n_of_deliverable_payloads_uv, - .send_payload = xpc_send_payload_uv, - .get_deliverable_payload = xpc_get_deliverable_payload_uv, - .received_payload = xpc_received_payload_uv, - .notify_senders_of_disconnect = xpc_notify_senders_of_disconnect_uv, -}; - -static int -xpc_init_mq_node(int nid) -{ - int cpu; - - cpus_read_lock(); - - for_each_cpu(cpu, cpumask_of_node(nid)) { - xpc_activate_mq_uv = - xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, nid, - XPC_ACTIVATE_IRQ_NAME, - xpc_handle_activate_IRQ_uv); - if (!IS_ERR(xpc_activate_mq_uv)) - break; - } - if (IS_ERR(xpc_activate_mq_uv)) { - cpus_read_unlock(); - return PTR_ERR(xpc_activate_mq_uv); - } - - for_each_cpu(cpu, cpumask_of_node(nid)) { - xpc_notify_mq_uv = - xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, nid, - XPC_NOTIFY_IRQ_NAME, - xpc_handle_notify_IRQ_uv); - if (!IS_ERR(xpc_notify_mq_uv)) - break; - } - if (IS_ERR(xpc_notify_mq_uv)) { - xpc_destroy_gru_mq_uv(xpc_activate_mq_uv); - cpus_read_unlock(); - return PTR_ERR(xpc_notify_mq_uv); - } - - cpus_read_unlock(); - return 0; -} - -int -xpc_init_uv(void) -{ - int nid; - int ret = 0; - - xpc_arch_ops = xpc_arch_ops_uv; - - if (sizeof(struct xpc_notify_mq_msghdr_uv) > XPC_MSG_HDR_MAX_SIZE) { - dev_err(xpc_part, "xpc_notify_mq_msghdr_uv is larger than %d\n", - XPC_MSG_HDR_MAX_SIZE); - return -E2BIG; - } - - if (xpc_mq_node < 0) - for_each_online_node(nid) { - ret = xpc_init_mq_node(nid); - - if (!ret) - break; - } - else - ret = xpc_init_mq_node(xpc_mq_node); - - if (ret < 0) - dev_err(xpc_part, "xpc_init_mq_node() returned error=%d\n", - -ret); - - return ret; -} - -void -xpc_exit_uv(void) -{ - xpc_destroy_gru_mq_uv(xpc_notify_mq_uv); - xpc_destroy_gru_mq_uv(xpc_activate_mq_uv); -} - -module_param(xpc_mq_node, int, 0); -MODULE_PARM_DESC(xpc_mq_node, "Node number on which to allocate message queues."); diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c deleted file mode 100644 index 1533b72d57b1..000000000000 --- a/drivers/misc/sgi-xp/xpnet.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (C) 1999-2009 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * Cross Partition Network Interface (XPNET) support - * - * XPNET provides a virtual network layered on top of the Cross - * Partition communication layer. - * - * XPNET provides direct point-to-point and broadcast-like support - * for an ethernet-like device. The ethernet broadcast medium is - * replaced with a point-to-point message structure which passes - * pointers to a DMA-capable block that a remote partition should - * retrieve and pass to the upper level networking layer. - * - */ - -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include "xp.h" - -/* - * The message payload transferred by XPC. - * - * buf_pa is the physical address where the DMA should pull from. - * - * NOTE: for performance reasons, buf_pa should _ALWAYS_ begin on a - * cacheline boundary. To accomplish this, we record the number of - * bytes from the beginning of the first cacheline to the first useful - * byte of the skb (leadin_ignore) and the number of bytes from the - * last useful byte of the skb to the end of the last cacheline - * (tailout_ignore). - * - * size is the number of bytes to transfer which includes the skb->len - * (useful bytes of the senders skb) plus the leadin and tailout - */ -struct xpnet_message { - u16 version; /* Version for this message */ - u16 embedded_bytes; /* #of bytes embedded in XPC message */ - u32 magic; /* Special number indicating this is xpnet */ - unsigned long buf_pa; /* phys address of buffer to retrieve */ - u32 size; /* #of bytes in buffer */ - u8 leadin_ignore; /* #of bytes to ignore at the beginning */ - u8 tailout_ignore; /* #of bytes to ignore at the end */ - unsigned char data; /* body of small packets */ -}; - -/* - * Determine the size of our message, the cacheline aligned size, - * and then the number of message will request from XPC. - * - * XPC expects each message to exist in an individual cacheline. - */ -#define XPNET_MSG_SIZE XPC_MSG_PAYLOAD_MAX_SIZE -#define XPNET_MSG_DATA_MAX \ - (XPNET_MSG_SIZE - offsetof(struct xpnet_message, data)) -#define XPNET_MSG_NENTRIES (PAGE_SIZE / XPC_MSG_MAX_SIZE) - -#define XPNET_MAX_KTHREADS (XPNET_MSG_NENTRIES + 1) -#define XPNET_MAX_IDLE_KTHREADS (XPNET_MSG_NENTRIES + 1) - -/* - * Version number of XPNET implementation. XPNET can always talk to versions - * with same major #, and never talk to versions with a different version. - */ -#define _XPNET_VERSION(_major, _minor) (((_major) << 4) | (_minor)) -#define XPNET_VERSION_MAJOR(_v) ((_v) >> 4) -#define XPNET_VERSION_MINOR(_v) ((_v) & 0xf) - -#define XPNET_VERSION _XPNET_VERSION(1, 0) /* version 1.0 */ -#define XPNET_VERSION_EMBED _XPNET_VERSION(1, 1) /* version 1.1 */ -#define XPNET_MAGIC 0x88786984 /* "XNET" */ - -#define XPNET_VALID_MSG(_m) \ - ((XPNET_VERSION_MAJOR(_m->version) == XPNET_VERSION_MAJOR(XPNET_VERSION)) \ - && (msg->magic == XPNET_MAGIC)) - -#define XPNET_DEVICE_NAME "xp0" - -/* - * When messages are queued with xpc_send_notify, a kmalloc'd buffer - * of the following type is passed as a notification cookie. When the - * notification function is called, we use the cookie to decide - * whether all outstanding message sends have completed. The skb can - * then be released. - */ -struct xpnet_pending_msg { - struct sk_buff *skb; - atomic_t use_count; -}; - -static struct net_device *xpnet_device; - -/* - * When we are notified of other partitions activating, we add them to - * our bitmask of partitions to which we broadcast. - */ -static unsigned long *xpnet_broadcast_partitions; -/* protect above */ -static DEFINE_SPINLOCK(xpnet_broadcast_lock); - -/* - * Since the Block Transfer Engine (BTE) is being used for the transfer - * and it relies upon cache-line size transfers, we need to reserve at - * least one cache-line for head and tail alignment. The BTE is - * limited to 8MB transfers. - * - * Testing has shown that changing MTU to greater than 64KB has no effect - * on TCP as the two sides negotiate a Max Segment Size that is limited - * to 64K. Other protocols May use packets greater than this, but for - * now, the default is 64KB. - */ -#define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES) -/* 68 comes from min TCP+IP+MAC header */ -#define XPNET_MIN_MTU 68 -/* 32KB has been determined to be the ideal */ -#define XPNET_DEF_MTU (0x8000UL) - -/* - * The partid is encapsulated in the MAC address beginning in the following - * octet and it consists of two octets. - */ -#define XPNET_PARTID_OCTET 2 - -/* Define the XPNET debug device structures to be used with dev_dbg() et al */ - -static struct device_driver xpnet_dbg_name = { - .name = "xpnet" -}; - -static struct device xpnet_dbg_subname = { - .init_name = "", /* set to "" */ - .driver = &xpnet_dbg_name -}; - -static struct device *xpnet = &xpnet_dbg_subname; - -/* - * Packet was recevied by XPC and forwarded to us. - */ -static void -xpnet_receive(short partid, int channel, struct xpnet_message *msg) -{ - struct sk_buff *skb; - void *dst; - enum xp_retval ret; - - if (!XPNET_VALID_MSG(msg)) { - /* - * Packet with a different XPC version. Ignore. - */ - xpc_received(partid, channel, (void *)msg); - - xpnet_device->stats.rx_errors++; - - return; - } - dev_dbg(xpnet, "received 0x%lx, %d, %d, %d\n", msg->buf_pa, msg->size, - msg->leadin_ignore, msg->tailout_ignore); - - /* reserve an extra cache line */ - skb = dev_alloc_skb(msg->size + L1_CACHE_BYTES); - if (!skb) { - dev_err(xpnet, "failed on dev_alloc_skb(%d)\n", - msg->size + L1_CACHE_BYTES); - - xpc_received(partid, channel, (void *)msg); - - xpnet_device->stats.rx_errors++; - - return; - } - - /* - * The allocated skb has some reserved space. - * In order to use xp_remote_memcpy(), we need to get the - * skb->data pointer moved forward. - */ - skb_reserve(skb, (L1_CACHE_BYTES - ((u64)skb->data & - (L1_CACHE_BYTES - 1)) + - msg->leadin_ignore)); - - /* - * Update the tail pointer to indicate data actually - * transferred. - */ - skb_put(skb, (msg->size - msg->leadin_ignore - msg->tailout_ignore)); - - /* - * Move the data over from the other side. - */ - if ((XPNET_VERSION_MINOR(msg->version) == 1) && - (msg->embedded_bytes != 0)) { - dev_dbg(xpnet, "copying embedded message. memcpy(0x%p, 0x%p, " - "%lu)\n", skb->data, &msg->data, - (size_t)msg->embedded_bytes); - - skb_copy_to_linear_data(skb, &msg->data, - (size_t)msg->embedded_bytes); - } else { - dst = (void *)((u64)skb->data & ~(L1_CACHE_BYTES - 1)); - dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t" - "xp_remote_memcpy(0x%p, 0x%p, %u)\n", dst, - (void *)msg->buf_pa, msg->size); - - ret = xp_remote_memcpy(xp_pa(dst), msg->buf_pa, msg->size); - if (ret != xpSuccess) { - /* - * !!! Need better way of cleaning skb. Currently skb - * !!! appears in_use and we can't just call - * !!! dev_kfree_skb. - */ - dev_err(xpnet, "xp_remote_memcpy(0x%p, 0x%p, 0x%x) " - "returned error=0x%x\n", dst, - (void *)msg->buf_pa, msg->size, ret); - - xpc_received(partid, channel, (void *)msg); - - xpnet_device->stats.rx_errors++; - - return; - } - } - - dev_dbg(xpnet, "<skb->head=0x%p skb->data=0x%p skb->tail=0x%p " - "skb->end=0x%p skb->len=%d\n", (void *)skb->head, - (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), - skb->len); - - skb->protocol = eth_type_trans(skb, xpnet_device); - skb->ip_summed = CHECKSUM_UNNECESSARY; - - dev_dbg(xpnet, "passing skb to network layer\n" - "\tskb->head=0x%p skb->data=0x%p skb->tail=0x%p " - "skb->end=0x%p skb->len=%d\n", - (void *)skb->head, (void *)skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->len); - - xpnet_device->stats.rx_packets++; - xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN; - - netif_rx(skb); - xpc_received(partid, channel, (void *)msg); -} - -/* - * This is the handler which XPC calls during any sort of change in - * state or message reception on a connection. - */ -static void -xpnet_connection_activity(enum xp_retval reason, short partid, int channel, - void *data, void *key) -{ - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(channel != XPC_NET_CHANNEL); - - switch (reason) { - case xpMsgReceived: /* message received */ - DBUG_ON(data == NULL); - - xpnet_receive(partid, channel, (struct xpnet_message *)data); - break; - - case xpConnected: /* connection completed to a partition */ - spin_lock_bh(&xpnet_broadcast_lock); - __set_bit(partid, xpnet_broadcast_partitions); - spin_unlock_bh(&xpnet_broadcast_lock); - - netif_carrier_on(xpnet_device); - - dev_dbg(xpnet, "%s connected to partition %d\n", - xpnet_device->name, partid); - break; - - default: - spin_lock_bh(&xpnet_broadcast_lock); - __clear_bit(partid, xpnet_broadcast_partitions); - spin_unlock_bh(&xpnet_broadcast_lock); - - if (bitmap_empty(xpnet_broadcast_partitions, - xp_max_npartitions)) { - netif_carrier_off(xpnet_device); - } - - dev_dbg(xpnet, "%s disconnected from partition %d\n", - xpnet_device->name, partid); - break; - } -} - -static int -xpnet_dev_open(struct net_device *dev) -{ - enum xp_retval ret; - - dev_dbg(xpnet, "calling xpc_connect(%d, 0x%p, NULL, %ld, %ld, %ld, " - "%ld)\n", XPC_NET_CHANNEL, xpnet_connection_activity, - (unsigned long)XPNET_MSG_SIZE, - (unsigned long)XPNET_MSG_NENTRIES, - (unsigned long)XPNET_MAX_KTHREADS, - (unsigned long)XPNET_MAX_IDLE_KTHREADS); - - ret = xpc_connect(XPC_NET_CHANNEL, xpnet_connection_activity, NULL, - XPNET_MSG_SIZE, XPNET_MSG_NENTRIES, - XPNET_MAX_KTHREADS, XPNET_MAX_IDLE_KTHREADS); - if (ret != xpSuccess) { - dev_err(xpnet, "ifconfig up of %s failed on XPC connect, " - "ret=%d\n", dev->name, ret); - - return -ENOMEM; - } - - dev_dbg(xpnet, "ifconfig up of %s; XPC connected\n", dev->name); - - return 0; -} - -static int -xpnet_dev_stop(struct net_device *dev) -{ - xpc_disconnect(XPC_NET_CHANNEL); - - dev_dbg(xpnet, "ifconfig down of %s; XPC disconnected\n", dev->name); - - return 0; -} - -/* - * Notification that the other end has received the message and - * DMA'd the skb information. At this point, they are done with - * our side. When all recipients are done processing, we - * release the skb and then release our pending message structure. - */ -static void -xpnet_send_completed(enum xp_retval reason, short partid, int channel, - void *__qm) -{ - struct xpnet_pending_msg *queued_msg = (struct xpnet_pending_msg *)__qm; - - DBUG_ON(queued_msg == NULL); - - dev_dbg(xpnet, "message to %d notified with reason %d\n", - partid, reason); - - if (atomic_dec_return(&queued_msg->use_count) == 0) { - dev_dbg(xpnet, "all acks for skb->head=-x%p\n", - (void *)queued_msg->skb->head); - - dev_kfree_skb_any(queued_msg->skb); - kfree(queued_msg); - } -} - -static void -xpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg, - u64 start_addr, u64 end_addr, u16 embedded_bytes, int dest_partid) -{ - u8 msg_buffer[XPNET_MSG_SIZE]; - struct xpnet_message *msg = (struct xpnet_message *)&msg_buffer; - u16 msg_size = sizeof(struct xpnet_message); - enum xp_retval ret; - - msg->embedded_bytes = embedded_bytes; - if (unlikely(embedded_bytes != 0)) { - msg->version = XPNET_VERSION_EMBED; - dev_dbg(xpnet, "calling memcpy(0x%p, 0x%p, 0x%lx)\n", - &msg->data, skb->data, (size_t)embedded_bytes); - skb_copy_from_linear_data(skb, &msg->data, - (size_t)embedded_bytes); - msg_size += embedded_bytes - 1; - } else { - msg->version = XPNET_VERSION; - } - msg->magic = XPNET_MAGIC; - msg->size = end_addr - start_addr; - msg->leadin_ignore = (u64)skb->data - start_addr; - msg->tailout_ignore = end_addr - (u64)skb_tail_pointer(skb); - msg->buf_pa = xp_pa((void *)start_addr); - - dev_dbg(xpnet, "sending XPC message to %d:%d\n" - "msg->buf_pa=0x%lx, msg->size=%u, " - "msg->leadin_ignore=%u, msg->tailout_ignore=%u\n", - dest_partid, XPC_NET_CHANNEL, msg->buf_pa, msg->size, - msg->leadin_ignore, msg->tailout_ignore); - - atomic_inc(&queued_msg->use_count); - - ret = xpc_send_notify(dest_partid, XPC_NET_CHANNEL, XPC_NOWAIT, msg, - msg_size, xpnet_send_completed, queued_msg); - if (unlikely(ret != xpSuccess)) - atomic_dec(&queued_msg->use_count); -} - -/* - * Network layer has formatted a packet (skb) and is ready to place it - * "on the wire". Prepare and send an xpnet_message to all partitions - * which have connected with us and are targets of this packet. - * - * MAC-NOTE: For the XPNET driver, the MAC address contains the - * destination partid. If the destination partid octets are 0xffff, - * this packet is to be broadcast to all connected partitions. - */ -static netdev_tx_t -xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct xpnet_pending_msg *queued_msg; - u64 start_addr, end_addr; - short dest_partid; - u16 embedded_bytes = 0; - - dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p " - "skb->end=0x%p skb->len=%d\n", (void *)skb->head, - (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), - skb->len); - - if (skb->data[0] == 0x33) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; /* nothing needed to be done */ - } - - /* - * The xpnet_pending_msg tracks how many outstanding - * xpc_send_notifies are relying on this skb. When none - * remain, release the skb. - */ - queued_msg = kmalloc_obj(struct xpnet_pending_msg, GFP_ATOMIC); - if (queued_msg == NULL) { - dev_warn(xpnet, "failed to kmalloc %ld bytes; dropping " - "packet\n", sizeof(struct xpnet_pending_msg)); - - dev->stats.tx_errors++; - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - /* get the beginning of the first cacheline and end of last */ - start_addr = ((u64)skb->data & ~(L1_CACHE_BYTES - 1)); - end_addr = L1_CACHE_ALIGN((u64)skb_tail_pointer(skb)); - - /* calculate how many bytes to embed in the XPC message */ - if (unlikely(skb->len <= XPNET_MSG_DATA_MAX)) { - /* skb->data does fit so embed */ - embedded_bytes = skb->len; - } - - /* - * Since the send occurs asynchronously, we set the count to one - * and begin sending. Any sends that happen to complete before - * we are done sending will not free the skb. We will be left - * with that task during exit. This also handles the case of - * a packet destined for a partition which is no longer up. - */ - atomic_set(&queued_msg->use_count, 1); - queued_msg->skb = skb; - - if (skb->data[0] == 0xff) { - /* we are being asked to broadcast to all partitions */ - for_each_set_bit(dest_partid, xpnet_broadcast_partitions, - xp_max_npartitions) { - - xpnet_send(skb, queued_msg, start_addr, end_addr, - embedded_bytes, dest_partid); - } - } else { - dest_partid = (short)skb->data[XPNET_PARTID_OCTET + 1]; - dest_partid |= (short)skb->data[XPNET_PARTID_OCTET + 0] << 8; - - if (dest_partid >= 0 && - dest_partid < xp_max_npartitions && - test_bit(dest_partid, xpnet_broadcast_partitions) != 0) { - - xpnet_send(skb, queued_msg, start_addr, end_addr, - embedded_bytes, dest_partid); - } - } - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - if (atomic_dec_return(&queued_msg->use_count) == 0) { - dev_kfree_skb(skb); - kfree(queued_msg); - } - - return NETDEV_TX_OK; -} - -/* - * Deal with transmit timeouts coming from the network layer. - */ -static void -xpnet_dev_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - dev->stats.tx_errors++; -} - -static const struct net_device_ops xpnet_netdev_ops = { - .ndo_open = xpnet_dev_open, - .ndo_stop = xpnet_dev_stop, - .ndo_start_xmit = xpnet_dev_hard_start_xmit, - .ndo_tx_timeout = xpnet_dev_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int __init -xpnet_init(void) -{ - u8 addr[ETH_ALEN]; - int result; - - if (!is_uv_system()) - return -ENODEV; - - dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME); - - xpnet_broadcast_partitions = bitmap_zalloc(xp_max_npartitions, - GFP_KERNEL); - if (xpnet_broadcast_partitions == NULL) - return -ENOMEM; - - /* - * use ether_setup() to init the majority of our device - * structure and then override the necessary pieces. - */ - xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, NET_NAME_UNKNOWN, - ether_setup); - if (xpnet_device == NULL) { - bitmap_free(xpnet_broadcast_partitions); - return -ENOMEM; - } - - netif_carrier_off(xpnet_device); - - xpnet_device->netdev_ops = &xpnet_netdev_ops; - xpnet_device->mtu = XPNET_DEF_MTU; - xpnet_device->min_mtu = XPNET_MIN_MTU; - xpnet_device->max_mtu = XPNET_MAX_MTU; - - memset(addr, 0, sizeof(addr)); - /* - * Multicast assumes the LSB of the first octet is set for multicast - * MAC addresses. We chose the first octet of the MAC to be unlikely - * to collide with any vendor's officially issued MAC. - */ - addr[0] = 0x02; /* locally administered, no OUI */ - - addr[XPNET_PARTID_OCTET + 1] = xp_partition_id; - addr[XPNET_PARTID_OCTET + 0] = (xp_partition_id >> 8); - eth_hw_addr_set(xpnet_device, addr); - - /* - * ether_setup() sets this to a multicast device. We are - * really not supporting multicast at this time. - */ - xpnet_device->flags &= ~IFF_MULTICAST; - - /* - * No need to checksum as it is a DMA transfer. The BTE will - * report an error if the data is not retrievable and the - * packet will be dropped. - */ - xpnet_device->features = NETIF_F_HW_CSUM; - - result = register_netdev(xpnet_device); - if (result != 0) { - free_netdev(xpnet_device); - bitmap_free(xpnet_broadcast_partitions); - } - - return result; -} - -module_init(xpnet_init); - -static void __exit -xpnet_exit(void) -{ - dev_info(xpnet, "unregistering network device %s\n", - xpnet_device[0].name); - - unregister_netdev(xpnet_device); - free_netdev(xpnet_device); - bitmap_free(xpnet_broadcast_partitions); -} - -module_exit(xpnet_exit); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_DESCRIPTION("Cross Partition Network adapter (XPNET)"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 1919d24c8236..c8990e83a65a 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -370,7 +370,7 @@ static const struct of_device_id sram_dt_ids[] = { { .compatible = "nvidia,tegra186-sysram", .data = &tegra_sysram_config }, { .compatible = "nvidia,tegra194-sysram", .data = &tegra_sysram_config }, { .compatible = "nvidia,tegra234-sysram", .data = &tegra_sysram_config }, - {} + { } }; static int sram_probe(struct platform_device *pdev) diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index 2aac83ec4a39..9e2d24f89457 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -718,7 +718,7 @@ static void fpc202_remove(struct i2c_client *client) static const struct of_device_id fpc202_of_match[] = { { .compatible = "ti,fpc202" }, - {} + { } }; MODULE_DEVICE_TABLE(of, fpc202_of_match); diff --git a/drivers/misc/tps6594-esm.c b/drivers/misc/tps6594-esm.c index 2fbd3fbdf713..50e121669c6d 100644 --- a/drivers/misc/tps6594-esm.c +++ b/drivers/misc/tps6594-esm.c @@ -62,7 +62,7 @@ static int tps6594_esm_probe(struct platform_device *pdev) tps6594_esm_isr, IRQF_ONESHOT, pdev->resource[i].name, pdev); if (ret) - return dev_err_probe(dev, ret, "Failed to request irq\n"); + return ret; } ret = regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG, diff --git a/drivers/misc/tps6594-pfsm.c b/drivers/misc/tps6594-pfsm.c index 44fa81d6cec2..c3a0d6367f2c 100644 --- a/drivers/misc/tps6594-pfsm.c +++ b/drivers/misc/tps6594-pfsm.c @@ -308,7 +308,7 @@ static int tps6594_pfsm_probe(struct platform_device *pdev) tps6594_pfsm_isr, IRQF_ONESHOT, pdev->resource[i].name, pdev); if (ret) - return dev_err_probe(dev, ret, "Failed to request irq\n"); + return ret; } platform_set_drvdata(pdev, pfsm); diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 03f19eda641e..c871ea860bb5 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -420,7 +420,7 @@ static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id tsl2550_id[] = { - { "tsl2550" }, + { .name = "tsl2550" }, { } }; MODULE_DEVICE_TABLE(i2c, tsl2550_id); diff --git a/drivers/misc/vcpu_stall_detector.c b/drivers/misc/vcpu_stall_detector.c index 26166357b255..1306b2c1ef99 100644 --- a/drivers/misc/vcpu_stall_detector.c +++ b/drivers/misc/vcpu_stall_detector.c @@ -224,8 +224,8 @@ static void vcpu_stall_detect_remove(struct platform_device *pdev) } static const struct of_device_id vcpu_stall_detect_of_match[] = { - { .compatible = "qemu,vcpu-stall-detector", }, - {} + { .compatible = "qemu,vcpu-stall-detector" }, + { } }; MODULE_DEVICE_TABLE(of, vcpu_stall_detect_of_match); diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c index fffe068a26eb..c3ef3b98c432 100644 --- a/drivers/misc/vmw_vmci/vmci_event.c +++ b/drivers/misc/vmw_vmci/vmci_event.c @@ -179,16 +179,16 @@ int vmci_event_subscribe(u32 event, } } + *new_subscription_id = sub->id; if (have_new_id) { list_add_rcu(&sub->node, &subscriber_array[event]); retval = VMCI_SUCCESS; } else { + kfree(sub); retval = VMCI_ERROR_NO_RESOURCES; } mutex_unlock(&subscriber_mutex); - - *new_subscription_id = sub->id; return retval; } EXPORT_SYMBOL_GPL(vmci_event_subscribe); diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 476af89e751b..83355c0ec1d2 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -960,8 +960,8 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) } static const struct pci_device_id vmci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), }, - { 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI) }, + { } }; MODULE_DEVICE_TABLE(pci, vmci_ids); diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c index 3135ba3a58ee..a19eed39c516 100644 --- a/drivers/misc/xilinx_sdfec.c +++ b/drivers/misc/xilinx_sdfec.c @@ -456,10 +456,25 @@ static int xsdfec_get_turbo(struct xsdfec_dev *xsdfec, void __user *arg) return err; } +static int xsdfec_ldpc_reg_addr(struct xsdfec_dev *xsdfec, u32 base, u32 high, + u32 offset, u32 *addr) +{ + if (high < base || offset > (high - base) / XSDFEC_LDPC_REG_JUMP) { + dev_dbg(xsdfec->dev, + "LDPC register offset %u outside space 0x%x-0x%x", + offset, base, high); + return -EINVAL; + } + + *addr = base + offset * XSDFEC_LDPC_REG_JUMP; + return 0; +} + static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize, u32 offset) { u32 wdata; + u32 addr; if (n < XSDFEC_REG0_N_MIN || n > XSDFEC_REG0_N_MAX || psize == 0 || (n > XSDFEC_REG0_N_MUL_P * psize) || n <= k || ((n % psize) != 0)) { @@ -476,17 +491,11 @@ static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize, k = k << XSDFEC_REG0_K_LSB; wdata = k | n; - if (XSDFEC_LDPC_CODE_REG0_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG0_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg0 space 0x%x", - XSDFEC_LDPC_CODE_REG0_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG0_ADDR_BASE, + XSDFEC_LDPC_CODE_REG0_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG0_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -494,6 +503,7 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize, u32 no_packing, u32 nm, u32 offset) { u32 wdata; + u32 addr; if (psize < XSDFEC_REG1_PSIZE_MIN || psize > XSDFEC_REG1_PSIZE_MAX) { dev_dbg(xsdfec->dev, "Psize is not in range"); @@ -510,17 +520,11 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize, nm = (nm << XSDFEC_REG1_NM_LSB) & XSDFEC_REG1_NM_MASK; wdata = nm | no_packing | psize; - if (XSDFEC_LDPC_CODE_REG1_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG1_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg1 space 0x%x", - XSDFEC_LDPC_CODE_REG1_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG1_ADDR_BASE, + XSDFEC_LDPC_CODE_REG1_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG1_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -529,6 +533,7 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc, u32 max_schedule, u32 offset) { u32 wdata; + u32 addr; if (nlayers < XSDFEC_REG2_NLAYERS_MIN || nlayers > XSDFEC_REG2_NLAYERS_MAX) { @@ -563,17 +568,11 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc, wdata = (max_schedule | no_final_parity | special_qc | norm_type | nmqc | nlayers); - if (XSDFEC_LDPC_CODE_REG2_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG2_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg2 space 0x%x", - XSDFEC_LDPC_CODE_REG2_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG2_ADDR_BASE, + XSDFEC_LDPC_CODE_REG2_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG2_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -581,20 +580,15 @@ static int xsdfec_reg3_write(struct xsdfec_dev *xsdfec, u8 sc_off, u8 la_off, u16 qc_off, u32 offset) { u32 wdata; + u32 addr; wdata = ((qc_off << XSDFEC_REG3_QC_OFF_LSB) | (la_off << XSDFEC_REG3_LA_OFF_LSB) | sc_off); - if (XSDFEC_LDPC_CODE_REG3_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG3_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg3 space 0x%x", - XSDFEC_LDPC_CODE_REG3_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG3_ADDR_BASE, + XSDFEC_LDPC_CODE_REG3_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG3_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -1390,10 +1384,8 @@ static int xsdfec_probe(struct platform_device *pdev) err = devm_request_threaded_irq(dev, xsdfec->irq, NULL, xsdfec_irq_thread, IRQF_ONESHOT, "xilinx-sdfec16", xsdfec); - if (err < 0) { - dev_err(dev, "unable to request IRQ%d", xsdfec->irq); + if (err < 0) goto err_xsdfec_dev; - } } err = ida_alloc(&dev_nrs, GFP_KERNEL); diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 74ddbd0f79b0..c36c2a4c2a0b 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -28,6 +28,107 @@ source "drivers/nvmem/layouts/Kconfig" # Devices +if NVMEM_SYSFS + +config EEPROM_AT24 + tristate "I2C EEPROMs / RAMs / ROMs from most vendors" + depends on I2C && SYSFS + select REGMAP + select REGMAP_I2C + help + Enable this driver to get read/write support to most I2C EEPROMs + and compatible devices like FRAMs, SRAMs, ROMs etc. After you + configure the driver to know about each chip on your target + board. Use these generic chip names, instead of vendor-specific + ones like at24c64, 24lc02 or fm24c04: + + 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, + 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048 + + Unless you like data loss puzzles, always be sure that any chip + you configure as a 24c32 (32 kbit) or larger is NOT really a + 24c16 (16 kbit) or smaller, and vice versa. Marking the chip + as read-only won't help recover from this. Also, if your chip + has any software write-protect mechanism you may want to review the + code to make sure this driver won't turn it on by accident. + + If you use this with an SMBus adapter instead of an I2C adapter, + full functionality is not available. Only smaller devices are + supported (24c16 and below, max 4 kByte). + + This driver can also be built as a module. If so, the module + will be called at24. + +config EEPROM_AT25 + tristate "SPI EEPROMs (FRAMs) from most vendors" + depends on SPI && SYSFS + select SPI_MEM + help + Enable this driver to get read/write support to most SPI EEPROMs + and Cypress FRAMs, + after you configure the board init code to know about each eeprom + on your target board. + + This driver can also be built as a module. If so, the module + will be called at25. + +config EEPROM_93XX46 + tristate "Microwire EEPROM 93XX46 support" + depends on SPI && SYSFS + select REGMAP + help + Driver for the microwire EEPROM chipsets 93xx46x. The driver + supports both read and write commands and also the command to + erase the whole EEPROM. + + This driver can also be built as a module. If so, the module + will be called eeprom_93xx46. + + If unsure, say N. + +config EEPROM_EE1004 + tristate "SPD EEPROMs on DDR4 memory modules" + depends on I2C && SYSFS + help + Enable this driver to get read support to SPD EEPROMs following + the JEDEC EE1004 standard. These are typically found on DDR4 + SDRAM memory modules. + + This driver can also be built as a module. If so, the module + will be called ee1004. + +config EEPROM_M24LR + tristate "STMicroelectronics M24LR RFID/NFC EEPROM support" + depends on I2C && SYSFS + select REGMAP_I2C + help + This enables support for STMicroelectronics M24LR RFID/NFC EEPROM + chips. These dual-interface devices expose two I2C addresses: + one for EEPROM memory access and another for control and system + configuration (e.g. UID, password handling). + + This driver provides a sysfs interface for control functions and + integrates with the nvmem subsystem for EEPROM access. + + To compile this driver as a module, choose M here: the + module will be called m24lr. + +endif # NVMEM_SYSFS + +config NVMEM_AIROHA_SMC_EFUSES + tristate "Airoha SMC eFuse support" + depends on ARM64 + depends on ARCH_AIROHA || COMPILE_TEST + depends on HAVE_ARM_SMCCC + default ARCH_AIROHA + help + Say y here to enable support for reading eFuses on Airoha AN7581 + SoCs. These are e.g. used to store factory programmed + calibration data required for the PCIe or the USB-C PHY or Thermal. + + This driver can also be built as a module. If so, the module will + be called nvmem-airoha-smc-efuses. + config NVMEM_AN8855_EFUSE tristate "Airoha AN8855 eFuse support" depends on COMPILE_TEST @@ -138,7 +239,7 @@ config NVMEM_JZ4780_EFUSE config NVMEM_LAN9662_OTPC tristate "Microchip LAN9662 OTP controller support" - depends on SOC_LAN966 || COMPILE_TEST + depends on SOC_LAN966 || ARCH_LAN969X || COMPILE_TEST depends on HAS_IOMEM help This driver enables the OTP controller available on Microchip LAN9662 @@ -262,7 +363,7 @@ config NVMEM_S32G_OCOTP Programmable memory pages. config NVMEM_QCOM_QFPROM - tristate "QCOM QFPROM Support" + tristate "Qualcomm QFPROM Support" depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM help @@ -273,7 +374,7 @@ config NVMEM_QCOM_QFPROM will be called nvmem_qfprom. config NVMEM_QCOM_SEC_QFPROM - tristate "QCOM SECURE QFPROM Support" + tristate "Qualcomm SECURE QFPROM Support" depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM depends on OF @@ -303,7 +404,6 @@ config NVMEM_RAVE_SP_EEPROM config NVMEM_RCAR_EFUSE tristate "Renesas R-Car Gen4 E-FUSE support" depends on (ARCH_RENESAS && ARM64) || COMPILE_TEST - depends on NVMEM help Enable support for reading the fuses in the E-FUSE or OTP non-volatile memory block on Renesas R-Car Gen4 SoCs. @@ -483,4 +583,4 @@ config NVMEM_QORIQ_EFUSE This driver can also be built as a module. If so, the module will be called nvmem_qoriq_efuse. -endif +endif # NVMEM diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 7252b8ec88d4..2bbfb9ff1885 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -10,6 +10,13 @@ nvmem_layouts-y := layouts.o obj-y += layouts/ # Devices +obj-$(CONFIG_EEPROM_AT24) += at24.o +obj-$(CONFIG_EEPROM_AT25) += at25.o +obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o +obj-$(CONFIG_EEPROM_EE1004) += ee1004.o +obj-$(CONFIG_EEPROM_M24LR) += m24lr.o +obj-$(CONFIG_NVMEM_AIROHA_SMC_EFUSES) += nvmem-airoha-smc-efuses.o +nvmem-airoha-smc-efuses-y := airoha-smc-efuses.o obj-$(CONFIG_NVMEM_AN8855_EFUSE) += nvmem-an8855-efuse.o nvmem-an8855-efuse-y := an8855-efuse.o obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o diff --git a/drivers/nvmem/airoha-smc-efuses.c b/drivers/nvmem/airoha-smc-efuses.c new file mode 100644 index 000000000000..e56a99f4aa1f --- /dev/null +++ b/drivers/nvmem/airoha-smc-efuses.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Christian Marangi <ansuelsmth@gmail.com> + */ + +#include <linux/arm-smccc.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#define AIROHA_SMC_EFUSE_FID 0x82000001 +#define AIROHA_SMC_EFUSE_SUB_ID_READ 0x44414552 + +#define AIROHA_EFUSE_CELLS 64 + +struct airoha_efuse_bank_priv { + u32 bank_index; +}; + +static int airoha_efuse_read(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct regmap *regmap = context; + + return regmap_bulk_read(regmap, offset, + val, bytes / sizeof(u32)); +} + +static int airoha_efuse_reg_read(void *context, unsigned int offset, + unsigned int *val) +{ + struct airoha_efuse_bank_priv *priv = context; + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(AIROHA_SMC_EFUSE_FID, + AIROHA_SMC_EFUSE_SUB_ID_READ, + priv->bank_index, offset, 0, 0, 0, 0, &res); + + /* check if SMC reported an error */ + if (res.a0) + return -EIO; + + *val = res.a1; + return 0; +} + +static int airoha_efuse_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + + for_each_child_of_node_scoped(dev->of_node, child) { + struct nvmem_config nvmem_config = { + .size = AIROHA_EFUSE_CELLS * sizeof(u32), + .stride = sizeof(u32), + .word_size = sizeof(u32), + .reg_read = airoha_efuse_read, + }; + struct regmap_config regmap_config = { + .reg_read = airoha_efuse_reg_read, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + struct airoha_efuse_bank_priv *priv; + struct nvmem_device *nvmem; + struct regmap *regmap; + const char *name; + u32 bank; + + ret = of_property_read_u32(child, "reg", &bank); + if (ret) + return ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + name = devm_kasprintf(dev, GFP_KERNEL, "airoha-efuse-%u", + bank); + if (!name) + return -ENOMEM; + + priv->bank_index = bank; + + regmap_config.name = name; + regmap = devm_regmap_init(dev, NULL, priv, + ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + nvmem_config.name = name; + nvmem_config.priv = regmap; + nvmem_config.dev = dev; + nvmem_config.id = bank; + nvmem_config.of_node = child; + nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + } + + return 0; +} + +static const struct of_device_id airoha_efuse_of_match[] = { + { .compatible = "airoha,an7581-efuses", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, airoha_efuse_of_match); + +static struct platform_driver airoha_efuse_driver = { + .probe = airoha_efuse_probe, + .driver = { + .name = "airoha-efuse", + .of_match_table = airoha_efuse_of_match, + }, +}; +module_platform_driver(airoha_efuse_driver); + +MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); +MODULE_DESCRIPTION("Driver for Airoha SMC eFUSEs"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/eeprom/at24.c b/drivers/nvmem/at24.c index 772c4d9fa651..772c4d9fa651 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/nvmem/at24.c diff --git a/drivers/misc/eeprom/at25.c b/drivers/nvmem/at25.c index bc2cfb75d9bb..bc2cfb75d9bb 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/nvmem/at25.c diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c index aaa6537798bf..9f77aee37121 100644 --- a/drivers/nvmem/brcm_nvram.c +++ b/drivers/nvmem/brcm_nvram.c @@ -84,6 +84,11 @@ static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device } WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len); + if (priv->data_len < sizeof(struct brcm_nvram_header)) { + dev_err(priv->dev, "NVRAM data too small (%zu)\n", priv->data_len); + return -EINVAL; + } + priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL); if (!priv->data) return -ENOMEM; @@ -187,9 +192,13 @@ static int brcm_nvram_parse(struct brcm_nvram *priv) } len = le32_to_cpu(header->len); - if (len > priv->nvmem_size) { - dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len, - priv->nvmem_size); + if (len < sizeof(*header)) { + dev_err(dev, "NVRAM length (%zd) too small\n", len); + return -EINVAL; + } + if (len > priv->data_len) { + dev_err(dev, "NVRAM length (%zd) exceeds data size (%zd)\n", len, + priv->data_len); return -EINVAL; } diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 2f971b059704..0556d140170a 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -6,6 +6,7 @@ * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com> */ +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/export.h> #include <linux/fs.h> @@ -13,6 +14,7 @@ #include <linux/init.h> #include <linux/kref.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> #include <linux/gpio/consumer.h> @@ -55,25 +57,34 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - if (nvmem->reg_read) - return nvmem->reg_read(nvmem->priv, offset, val, bytes); + struct nvmem_operations *ops = nvmem->ops; - return -EINVAL; + if (!ops->reg_read) + return -EOPNOTSUPP; + + return ops->reg_read(nvmem->priv, offset, val, bytes); } static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - int ret; + struct nvmem_operations *ops = nvmem->ops; + int ret, wr_ok; - if (nvmem->reg_write) { - gpiod_set_value_cansleep(nvmem->wp_gpio, 0); - ret = nvmem->reg_write(nvmem->priv, offset, val, bytes); - gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + if (!ops->reg_write) + return -EOPNOTSUPP; + + ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 0); + if (ret) + return ret; + + wr_ok = ops->reg_write(nvmem->priv, offset, val, bytes); + + ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + if (ret) return ret; - } - return -EINVAL; + return wr_ok; } static int nvmem_access_with_keepouts(struct nvmem_device *nvmem, @@ -231,13 +242,12 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, count = round_down(count, nvmem->word_size); - if (!nvmem->reg_read) - return -EPERM; - rc = nvmem_reg_read(nvmem, pos, buf, count); - - if (rc) + if (rc) { + if (rc == -EOPNOTSUPP) + return -EPERM; return rc; + } return count; } @@ -264,19 +274,23 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, count = round_down(count, nvmem->word_size); - if (!nvmem->reg_write || nvmem->read_only) + if (nvmem->read_only) return -EPERM; rc = nvmem_reg_write(nvmem, pos, buf, count); - - if (rc) + if (rc) { + if (rc == -EOPNOTSUPP) + return -EPERM; return rc; + } return count; } static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) { + struct nvmem_operations *ops = nvmem->ops; + umode_t mode = 0400; if (!nvmem->root_only) @@ -285,10 +299,10 @@ static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) if (!nvmem->read_only) mode |= 0200; - if (!nvmem->reg_write) + if (!ops->reg_write) mode &= ~0200; - if (!nvmem->reg_read) + if (!ops->reg_read) mode &= ~0444; return mode; @@ -319,6 +333,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj, { struct device *dev = kobj_to_dev(kobj); struct nvmem_device *nvmem = to_nvmem_device(dev); + struct nvmem_operations *ops = nvmem->ops; /* * If the device has no .reg_write operation, do not allow @@ -327,7 +342,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj, * can be forced into read-write mode using the 'force_ro' * attribute. */ - if (attr == &dev_attr_force_ro.attr && !nvmem->reg_write) + if (attr == &dev_attr_force_ro.attr && !ops->reg_write) return 0; /* Attribute not visible */ return attr->mode; @@ -445,10 +460,9 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, return 0; } -static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, - const struct nvmem_config *config) +static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem) { - if (config->compat) + if (nvmem->flags & FLAG_COMPAT) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); } @@ -461,27 +475,23 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) const struct bin_attribute **pattrs; struct bin_attribute *attrs; unsigned int ncells = 0, i = 0; - int ret = 0; + int ret; - mutex_lock(&nvmem_mutex); + guard(mutex)(&nvmem_mutex); if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) - goto unlock_mutex; + return 0; /* Allocate an array of attributes with a sentinel */ ncells = list_count_nodes(&nvmem->cells); pattrs = devm_kcalloc(&nvmem->dev, ncells + 1, sizeof(struct bin_attribute *), GFP_KERNEL); - if (!pattrs) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!pattrs) + return -ENOMEM; attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL); - if (!attrs) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!attrs) + return -ENOMEM; /* Initialize each attribute to take the name and size of the cell */ list_for_each_entry(entry, &nvmem->cells, node) { @@ -494,10 +504,8 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) attrs[i].size = entry->bytes; attrs[i].read = &nvmem_cell_attr_read; attrs[i].private = entry; - if (!attrs[i].attr.name) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!attrs[i].attr.name) + return -ENOMEM; pattrs[i] = &attrs[i]; i++; @@ -507,13 +515,10 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) ret = device_add_group(&nvmem->dev, &group); if (ret) - goto unlock_mutex; + return ret; nvmem->sysfs_cells_populated = true; -unlock_mutex: - mutex_unlock(&nvmem_mutex); - return ret; } @@ -524,8 +529,7 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, { return -ENOSYS; } -static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, - const struct nvmem_config *config) +static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem) { } @@ -537,6 +541,7 @@ static void nvmem_release(struct device *dev) ida_free(&nvmem_ida, nvmem->id); gpiod_put(nvmem->wp_gpio); + kfree(nvmem->ops); kfree(nvmem); } @@ -551,9 +556,8 @@ static const struct bus_type nvmem_bus_type = { static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell) { blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell); - mutex_lock(&nvmem_mutex); - list_del(&cell->node); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) + list_del(&cell->node); of_node_put(cell->np); kfree_const(cell->name); kfree(cell); @@ -569,9 +573,8 @@ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem) static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell) { - mutex_lock(&nvmem_mutex); - list_add_tail(&cell->node, &cell->nvmem->cells); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) + list_add_tail(&cell->node, &cell->nvmem->cells); blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell); } @@ -721,14 +724,14 @@ nvmem_find_cell_entry_by_name(struct nvmem_device *nvmem, const char *cell_id) { struct nvmem_cell_entry *iter, *cell = NULL; - mutex_lock(&nvmem_mutex); + guard(mutex)(&nvmem_mutex); + list_for_each_entry(iter, &nvmem->cells, node) { if (strcmp(cell_id, iter->name) == 0) { cell = iter; break; } } - mutex_unlock(&nvmem_mutex); return cell; } @@ -883,6 +886,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregister); struct nvmem_device *nvmem_register(const struct nvmem_config *config) { + struct nvmem_operations *ops; struct nvmem_device *nvmem; int rval; @@ -896,8 +900,15 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (!nvmem) return ERR_PTR(-ENOMEM); + ops = kzalloc_obj(*ops); + if (!ops) { + kfree(nvmem); + return ERR_PTR(-ENOMEM); + } + rval = ida_alloc(&nvmem_ida, GFP_KERNEL); if (rval < 0) { + kfree(ops); kfree(nvmem); return ERR_PTR(rval); } @@ -907,6 +918,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->dev.type = &nvmem_provider_type; nvmem->dev.bus = &nvmem_bus_type; nvmem->dev.parent = config->dev; + nvmem->ops = ops; device_initialize(&nvmem->dev); @@ -923,6 +935,9 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) INIT_LIST_HEAD(&nvmem->cells); nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; + ops->reg_read = config->reg_read; + ops->reg_write = config->reg_write; + nvmem->owner = config->owner; if (!nvmem->owner && config->dev->driver) nvmem->owner = config->dev->driver->owner; @@ -932,8 +947,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->root_only = config->root_only; nvmem->priv = config->priv; nvmem->type = config->type; - nvmem->reg_read = config->reg_read; - nvmem->reg_write = config->reg_write; nvmem->keepout = config->keepout; nvmem->nkeepout = config->nkeepout; if (config->of_node) @@ -959,7 +972,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_put_device; nvmem->read_only = device_property_present(config->dev, "read-only") || - config->read_only || !nvmem->reg_write; + config->read_only || !ops->reg_write; #ifdef CONFIG_NVMEM_SYSFS nvmem->dev.groups = nvmem_dev_groups; @@ -999,6 +1012,10 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (rval) goto err_remove_dev; + /* If the device has WP GPIO, default to read-only */ + if (nvmem->wp_gpio) + nvmem->read_only = true; + #ifdef CONFIG_NVMEM_SYSFS rval = nvmem_populate_sysfs_cells(nvmem); if (rval) @@ -1017,8 +1034,7 @@ err_remove_dev: device_del(&nvmem->dev); err_remove_cells: nvmem_device_remove_all_cells(nvmem); - if (config->compat) - nvmem_sysfs_remove_compat(nvmem, config); + nvmem_sysfs_remove_compat(nvmem); err_put_device: put_device(&nvmem->dev); @@ -1034,8 +1050,7 @@ static void nvmem_device_release(struct kref *kref) blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem); - if (nvmem->flags & FLAG_COMPAT) - device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + nvmem_sysfs_remove_compat(nvmem); nvmem_device_remove_all_cells(nvmem); nvmem_destroy_layout(nvmem); @@ -1088,17 +1103,17 @@ struct nvmem_device *devm_nvmem_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_nvmem_register); -static struct nvmem_device *__nvmem_device_get(void *data, +static struct nvmem_device *nvmem_device_match(void *data, int (*match)(struct device *dev, const void *data)) { struct nvmem_device *nvmem = NULL; struct device *dev; - mutex_lock(&nvmem_mutex); - dev = bus_find_device(&nvmem_bus_type, NULL, data, match); - if (dev) - nvmem = to_nvmem_device(dev); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) { + dev = bus_find_device(&nvmem_bus_type, NULL, data, match); + if (dev) + nvmem = to_nvmem_device(dev); + } if (!nvmem) return ERR_PTR(-EPROBE_DEFER); @@ -1116,13 +1131,6 @@ static struct nvmem_device *__nvmem_device_get(void *data, return nvmem; } -static void __nvmem_device_put(struct nvmem_device *nvmem) -{ - put_device(&nvmem->dev); - module_put(nvmem->owner); - kref_put(&nvmem->refcnt, nvmem_device_release); -} - #if IS_ENABLED(CONFIG_OF) /** * of_nvmem_device_get() - Get nvmem device from a given id @@ -1147,7 +1155,7 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) if (!nvmem_np) return ERR_PTR(-ENOENT); - nvmem = __nvmem_device_get(nvmem_np, device_match_of_node); + nvmem = nvmem_device_match(nvmem_np, device_match_of_node); of_node_put(nvmem_np); return nvmem; } @@ -1175,7 +1183,7 @@ struct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name) } - return __nvmem_device_get((void *)dev_name, device_match_name); + return nvmem_device_match((void *)dev_name, device_match_name); } EXPORT_SYMBOL_GPL(nvmem_device_get); @@ -1191,7 +1199,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_get); struct nvmem_device *nvmem_device_find(void *data, int (*match)(struct device *dev, const void *data)) { - return __nvmem_device_get(data, match); + return nvmem_device_match(data, match); } EXPORT_SYMBOL_GPL(nvmem_device_find); @@ -1235,7 +1243,9 @@ EXPORT_SYMBOL_GPL(devm_nvmem_device_put); */ void nvmem_device_put(struct nvmem_device *nvmem) { - __nvmem_device_put(nvmem); + put_device(&nvmem->dev); + module_put(nvmem->owner); + kref_put(&nvmem->refcnt, nvmem_device_release); } EXPORT_SYMBOL_GPL(nvmem_device_put); @@ -1308,35 +1318,32 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) dev_id = dev_name(dev); - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); list_for_each_entry(lookup, &nvmem_lookup_list, node) { if ((strcmp(lookup->dev_id, dev_id) == 0) && (strcmp(lookup->con_id, con_id) == 0)) { /* This is the right entry. */ - nvmem = __nvmem_device_get((void *)lookup->nvmem_name, + nvmem = nvmem_device_match((void *)lookup->nvmem_name, device_match_name); - if (IS_ERR(nvmem)) { + if (IS_ERR(nvmem)) /* Provider may not be registered yet. */ - cell = ERR_CAST(nvmem); - break; - } + return ERR_CAST(nvmem); cell_entry = nvmem_find_cell_entry_by_name(nvmem, lookup->cell_name); if (!cell_entry) { - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); cell = ERR_PTR(-ENOENT); } else { cell = nvmem_create_cell(cell_entry, con_id, 0); if (IS_ERR(cell)) - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } break; } } - mutex_unlock(&nvmem_lookup_mutex); return cell; } @@ -1350,18 +1357,16 @@ static void nvmem_layout_module_put(struct nvmem_device *nvmem) static struct nvmem_cell_entry * nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np) { - struct nvmem_cell_entry *iter, *cell = NULL; + struct nvmem_cell_entry *cell; - mutex_lock(&nvmem_mutex); - list_for_each_entry(iter, &nvmem->cells, node) { - if (np == iter->np) { - cell = iter; - break; - } + guard(mutex)(&nvmem_mutex); + + list_for_each_entry(cell, &nvmem->cells, node) { + if (np == cell->np) + return cell; } - mutex_unlock(&nvmem_mutex); - return cell; + return NULL; } static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem) @@ -1431,7 +1436,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) } } - nvmem = __nvmem_device_get(nvmem_np, device_match_of_node); + nvmem = nvmem_device_match(nvmem_np, device_match_of_node); of_node_put(nvmem_np); if (IS_ERR(nvmem)) { of_node_put(cell_np); @@ -1441,7 +1446,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) ret = nvmem_layout_module_get_optional(nvmem); if (ret) { of_node_put(cell_np); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); return ERR_PTR(ret); } @@ -1450,14 +1455,14 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) if (!cell_entry) { nvmem_layout_module_put(nvmem); ret = nvmem->layout ? -EPROBE_DEFER : -ENOENT; - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); return ERR_PTR(ret); } cell = nvmem_create_cell(cell_entry, id, cell_index); if (IS_ERR(cell)) { nvmem_layout_module_put(nvmem); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } return cell; @@ -1572,7 +1577,7 @@ void nvmem_cell_put(struct nvmem_cell *cell) kfree(cell); nvmem_layout_module_put(nvmem); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } EXPORT_SYMBOL_GPL(nvmem_cell_put); @@ -2094,10 +2099,10 @@ void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) { int i; - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) list_add_tail(&entries[i].node, &nvmem_lookup_list); - mutex_unlock(&nvmem_lookup_mutex); } EXPORT_SYMBOL_GPL(nvmem_add_cell_lookups); @@ -2112,10 +2117,10 @@ void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) { int i; - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) list_del(&entries[i].node); - mutex_unlock(&nvmem_lookup_mutex); } EXPORT_SYMBOL_GPL(nvmem_del_cell_lookups); diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/nvmem/ee1004.c index 923f404a44c0..923f404a44c0 100644 --- a/drivers/misc/eeprom/ee1004.c +++ b/drivers/nvmem/ee1004.c diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/nvmem/eeprom_93xx46.c index f9c3ab52c2f9..f9c3ab52c2f9 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/nvmem/eeprom_93xx46.c diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h index f6d452b6a28f..4e610deeaa7b 100644 --- a/drivers/nvmem/internals.h +++ b/drivers/nvmem/internals.h @@ -7,10 +7,15 @@ #include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> +/* Hold pointers to callbacks owned by the nvmem provider module. */ +struct nvmem_operations { + nvmem_reg_read_t reg_read; + nvmem_reg_write_t reg_write; +}; + struct nvmem_device { struct module *owner; struct device dev; - struct list_head node; int stride; int word_size; int id; @@ -27,10 +32,9 @@ struct nvmem_device { struct nvmem_cell_info *cell); const struct nvmem_keepout *keepout; unsigned int nkeepout; - nvmem_reg_read_t reg_read; - nvmem_reg_write_t reg_write; struct gpio_desc *wp_gpio; struct nvmem_layout *layout; + struct nvmem_operations *ops; void *priv; bool sysfs_cells_populated; }; diff --git a/drivers/nvmem/lan9662-otpc.c b/drivers/nvmem/lan9662-otpc.c index 56fc19f092a7..62d1d6381bf8 100644 --- a/drivers/nvmem/lan9662-otpc.c +++ b/drivers/nvmem/lan9662-otpc.c @@ -27,7 +27,6 @@ #define OTP_OTP_STATUS_OTP_CPUMPEN BIT(1) #define OTP_OTP_STATUS_OTP_BUSY BIT(0) -#define OTP_MEM_SIZE 8192 #define OTP_SLEEP_US 10 #define OTP_TIMEOUT_US 500000 @@ -176,7 +175,6 @@ static struct nvmem_config otp_config = { .word_size = 1, .reg_read = lan9662_otp_read, .reg_write = lan9662_otp_write, - .size = OTP_MEM_SIZE, }; static int lan9662_otp_probe(struct platform_device *pdev) @@ -196,6 +194,7 @@ static int lan9662_otp_probe(struct platform_device *pdev) otp_config.priv = otp; otp_config.dev = dev; + otp_config.size = (uintptr_t) device_get_match_data(dev); nvmem = devm_nvmem_register(dev, &otp_config); @@ -203,7 +202,14 @@ static int lan9662_otp_probe(struct platform_device *pdev) } static const struct of_device_id lan9662_otp_match[] = { - { .compatible = "microchip,lan9662-otpc", }, + { + .compatible = "microchip,lan9662-otpc", + .data = (const void *) SZ_8K, + }, + { + .compatible = "microchip,lan9691-otpc", + .data = (const void *) SZ_16K, + }, { }, }; MODULE_DEVICE_TABLE(of, lan9662_otp_match); diff --git a/drivers/nvmem/layouts/u-boot-env.c b/drivers/nvmem/layouts/u-boot-env.c index f27f387bb52a..33ec2350386f 100644 --- a/drivers/nvmem/layouts/u-boot-env.c +++ b/drivers/nvmem/layouts/u-boot-env.c @@ -38,9 +38,6 @@ static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, i { u8 mac[ETH_ALEN]; - if (bytes != MAC_ADDR_STR_LEN) - return -EINVAL; - if (!mac_pton(buf, mac)) return -EINVAL; @@ -75,7 +72,7 @@ static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem info.offset = data_offset + value - data; info.bytes = strlen(value); info.np = of_get_child_by_name(dev->of_node, info.name); - if (!strcmp(var, "ethaddr")) { + if (!strcmp(var, "ethaddr") && info.bytes == MAC_ADDR_STR_LEN) { info.raw_len = strlen(value); info.bytes = ETH_ALEN; info.read_post_process = u_boot_env_read_post_process_ethaddr; diff --git a/drivers/misc/eeprom/m24lr.c b/drivers/nvmem/m24lr.c index 7a9fd45a8e46..7a9fd45a8e46 100644 --- a/drivers/misc/eeprom/m24lr.c +++ b/drivers/nvmem/m24lr.c diff --git a/drivers/nvmem/nintendo-otp.c b/drivers/nvmem/nintendo-otp.c index 4440d4e5fb83..a4d986e4588e 100644 --- a/drivers/nvmem/nintendo-otp.c +++ b/drivers/nvmem/nintendo-otp.c @@ -16,7 +16,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/nvmem-provider.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> #define HW_OTPCMD 0 @@ -73,8 +73,7 @@ MODULE_DEVICE_TABLE(of, nintendo_otp_of_table); static int nintendo_otp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *of_id = - of_match_device(nintendo_otp_of_table, dev); + const struct nintendo_otp_devtype_data *data; struct nvmem_device *nvmem; struct nintendo_otp_priv *priv; @@ -94,8 +93,8 @@ static int nintendo_otp_probe(struct platform_device *pdev) if (IS_ERR(priv->regs)) return PTR_ERR(priv->regs); - if (of_id->data) { - const struct nintendo_otp_devtype_data *data = of_id->data; + data = of_device_get_match_data(dev); + if (data) { config.name = data->name; config.size = data->num_banks * BANK_SIZE; } diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c index 0ec78b5e19e7..2c0feb036f3f 100644 --- a/drivers/nvmem/rockchip-otp.c +++ b/drivers/nvmem/rockchip-otp.c @@ -78,9 +78,9 @@ struct rockchip_data { struct rockchip_otp { struct device *dev; void __iomem *base; - struct clk_bulk_data *clks; struct reset_control *rst; const struct rockchip_data *data; + struct clk_bulk_data clks[]; }; static int rockchip_otp_reset(struct rockchip_otp *otp) @@ -424,7 +424,7 @@ static int rockchip_otp_probe(struct platform_device *pdev) if (!data) return dev_err_probe(dev, -EINVAL, "failed to get match data\n"); - otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp), + otp = devm_kzalloc(&pdev->dev, struct_size(otp, clks, data->num_clks), GFP_KERNEL); if (!otp) return -ENOMEM; @@ -436,11 +436,6 @@ static int rockchip_otp_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(otp->base), "failed to ioremap resource\n"); - otp->clks = devm_kcalloc(dev, data->num_clks, sizeof(*otp->clks), - GFP_KERNEL); - if (!otp->clks) - return -ENOMEM; - for (i = 0; i < data->num_clks; ++i) otp->clks[i].id = data->clks[i]; diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index c75abdd8ef25..bccfe75fe6e5 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2745,116 +2745,128 @@ static struct parport_pc_pci { static const struct pci_device_id parport_pc_pci_tbl[] = { /* Super-IO onboard chips */ - { 0x1106, 0x0686, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_686a }, - { 0x1106, 0x8231, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_8231 }, - { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_ite_8872 }, + { PCI_DEVICE(0x1106, 0x0686), + .driver_data = sio_via_686a }, + { PCI_DEVICE(0x1106, 0x8231), + .driver_data = sio_via_8231 }, + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8872), + .driver_data = sio_ite_8872 }, /* PCI cards */ - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_20x }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_a }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_b }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, boca_ioppar }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0, 0, plx_9050 }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1P_10x), + .driver_data = siig_1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P_10x), + .driver_data = siig_2p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1P_20x), + .driver_data = siig_1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P_20x), + .driver_data = siig_2p_20x }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_PARALLEL), + .driver_data = lava_parallel }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A), + .driver_data = lava_parallel_dual_a }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B), + .driver_data = lava_parallel_dual_b }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR), + .driver_data = boca_ioppar }, + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014), + .driver_data = plx_9050 }, /* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/ - { 0x1409, 0x7268, 0x1409, 0x0101, 0, 0, timedia_4006a }, - { 0x1409, 0x7268, 0x1409, 0x0102, 0, 0, timedia_4014 }, - { 0x1409, 0x7268, 0x1409, 0x0103, 0, 0, timedia_4008a }, - { 0x1409, 0x7268, 0x1409, 0x0104, 0, 0, timedia_4018 }, - { 0x1409, 0x7268, 0x1409, 0x9018, 0, 0, timedia_9018a }, - { PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_2P_EPP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_2p_epp }, - { PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_1P_ECP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_1p_ecp }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_010L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_010l }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0101), + .driver_data = timedia_4006a }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0102), + .driver_data = timedia_4014 }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0103), + .driver_data = timedia_4008a }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0104), + .driver_data = timedia_4018 }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x9018), + .driver_data = timedia_9018a }, + { PCI_VDEVICE(SYBA, PCI_DEVICE_ID_SYBA_2P_EPP), + .driver_data = syba_2p_epp }, + { PCI_VDEVICE(SYBA, PCI_DEVICE_ID_SYBA_1P_ECP), + .driver_data = syba_1p_ecp }, + { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_010L), + .driver_data = titan_010l }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ /* AFAVLAB_TK9902 */ - { 0x14db, 0x2120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1p}, - { 0x14db, 0x2121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2p}, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_952 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_954 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_840 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_AKS, PCI_DEVICE_ID_AKS_ALADDINCARD, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, aks_0100 }, - { 0x14f2, 0x0121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, mobility_pp }, + { PCI_DEVICE(0x14db, 0x2120), + .driver_data = avlab_1p }, + { PCI_DEVICE(0x14db, 0x2121), + .driver_data = avlab_2p }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP), + .driver_data = oxsemi_952 }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP), + .driver_data = oxsemi_954 }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840), + .driver_data = oxsemi_840 }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(AKS, PCI_DEVICE_ID_AKS_ALADDINCARD), + .driver_data = aks_0100 }, + { PCI_DEVICE(0x14f2, 0x0121), + .driver_data = mobility_pp }, /* NetMos communication controllers */ - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x2000, 0, 0, netmos_9900 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9705, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9705 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9715 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9755, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9755 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9805, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, - 0xA000, 0x2000, 0, 0, netmos_9901 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x1000, 0, 0, netmos_9865 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x2000, 0, 0, netmos_9865 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x2000), + .driver_data = netmos_9900 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9705), + .driver_data = netmos_9705 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9715), + .driver_data = netmos_9715 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9755), + .driver_data = netmos_9755 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9805), + .driver_data = netmos_9805 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9815), + .driver_data = netmos_9815 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9901, 0xA000, 0x2000), + .driver_data = netmos_9901 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, 0xA000, 0x1000), + .driver_data = netmos_9865 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, 0xA000, 0x2000), + .driver_data = netmos_9865 }, /* ASIX AX99100 PCIe to Multi I/O Controller */ - { PCI_VENDOR_ID_ASIX, PCI_DEVICE_ID_ASIX_AX99100, - 0xA000, 0x2000, 0, 0, asix_ax99100 }, + { PCI_VDEVICE_SUB(ASIX, PCI_DEVICE_ID_ASIX_AX99100, 0xA000, 0x2000), + .driver_data = asix_ax99100 }, /* Quatech SPPXP-100 Parallel port PCI ExpressCard */ - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 }, + { PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100), + .driver_data = quatech_sppxp100 }, /* WCH CH382L PCI-E single parallel port card */ - { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l }, + { PCI_DEVICE_SUB(0x1c00, 0x3050, 0x1c00, 0x3050), + .driver_data = wch_ch382l }, /* Brainboxes IX-500/550 */ - { PCI_VENDOR_ID_INTASHIELD, 0x402a, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, + { PCI_VDEVICE(INTASHIELD, 0x402a), + .driver_data = oxsemi_pcie_pport }, /* Brainboxes UC-146/UC-157 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0be1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0be2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 }, + { PCI_VDEVICE(INTASHIELD, 0x0be1), + .driver_data = brainboxes_uc146 }, + { PCI_VDEVICE(INTASHIELD, 0x0be2), + .driver_data = brainboxes_uc146 }, /* Brainboxes PX-146/PX-257 */ - { PCI_VENDOR_ID_INTASHIELD, 0x401c, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, + { PCI_VDEVICE(INTASHIELD, 0x401c), + .driver_data = oxsemi_pcie_pport }, /* Brainboxes PX-203 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4007, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px203 }, + { PCI_VDEVICE(INTASHIELD, 0x4007), + .driver_data = brainboxes_px203 }, /* Brainboxes PX-475 */ - { PCI_VENDOR_ID_INTASHIELD, 0x401f, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { 0, } /* terminate list */ + { PCI_VDEVICE(INTASHIELD, 0x401f), + .driver_data = oxsemi_pcie_pport }, + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl); diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 24d4f3a3ec3d..4d4140c8584d 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -170,158 +170,176 @@ static struct parport_pc_pci cards[] = { static struct pci_device_id parport_serial_pci_tbl[] = { /* PCI cards */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - 0x1000, 0x0020, 0, 0, netmos_9855_2p }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - 0x1000, 0x0022, 0, 0, netmos_9855_2p }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3011, 0, 0, netmos_9900 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3012, 0, 0, netmos_9900 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3020, 0, 0, netmos_9900_2p }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, - 0xA000, 0x2000, 0, 0, netmos_99xx_1p }, + { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_110L), + .driver_data = titan_110l }, + { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_210L), + .driver_data = titan_210l }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9735), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9745), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9835), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9845), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9855, 0x1000, 0x0020), + .driver_data = netmos_9855_2p }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9855, 0x1000, 0x0022), + .driver_data = netmos_9855_2p }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9855), + .driver_data = netmos_9855 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3011), + .driver_data = netmos_9900 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3012), + .driver_data = netmos_9900 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3020), + .driver_data = netmos_9900_2p }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9912, 0xA000, 0x2000), + .driver_data = netmos_99xx_1p }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ - { PCI_VENDOR_ID_AFAVLAB, 0x2110, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2111, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2112, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2140, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2141, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2142, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2160, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2161, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2162, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, + { PCI_VDEVICE(AFAVLAB, 0x2110), + .driver_data = avlab_1s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2111), + .driver_data = avlab_1s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2112), + .driver_data = avlab_1s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2140), + .driver_data = avlab_1s2p }, + { PCI_VDEVICE(AFAVLAB, 0x2141), + .driver_data = avlab_1s2p }, + { PCI_VDEVICE(AFAVLAB, 0x2142), + .driver_data = avlab_1s2p }, + { PCI_VDEVICE(AFAVLAB, 0x2160), + .driver_data = avlab_2s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2161), + .driver_data = avlab_2s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2162), + .driver_data = avlab_2s1p }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550), + .driver_data = siig_1s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650), + .driver_data = siig_1s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850), + .driver_data = siig_1s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550), + .driver_data = siig_2s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650), + .driver_data = siig_2s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850), + .driver_data = siig_2s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550), + .driver_data = siig_2p1s_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650), + .driver_data = siig_2p1s_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850), + .driver_data = siig_2p1s_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550), + .driver_data = siig_2s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650), + .driver_data = siig_1s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850), + .driver_data = siig_1s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550), + .driver_data = siig_2s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650), + .driver_data = siig_2s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850), + .driver_data = siig_2s1p_20x }, /* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/ - { 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a }, - { 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h }, - { 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h }, - { 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a }, - { 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a }, - { 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a }, - { 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a }, - { 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u }, - { 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a }, - { 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u }, - { 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r }, - { 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s }, - { 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d }, - { 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e }, - { 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f }, - { 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a }, - { 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b }, - { 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4078), + .driver_data = timedia_4078a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4079), + .driver_data = timedia_4079h }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4085), + .driver_data = timedia_4085h }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4088), + .driver_data = timedia_4088a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4089), + .driver_data = timedia_4089a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4095), + .driver_data = timedia_4095a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4096), + .driver_data = timedia_4096a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5078), + .driver_data = timedia_4078u }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5079), + .driver_data = timedia_4079a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5085), + .driver_data = timedia_4085u }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x6079), + .driver_data = timedia_4079r }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x7079), + .driver_data = timedia_4079s }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x8079), + .driver_data = timedia_4079d }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x9079), + .driver_data = timedia_4079e }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xa079), + .driver_data = timedia_4079f }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xb079), + .driver_data = timedia_9079a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xc079), + .driver_data = timedia_9079b }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xd079), + .driver_data = timedia_9079c }, /* WCH CARDS */ - { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_1S1P, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p }, - { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1P, - 0x4348, 0x3253, 0, 0, wch_ch353_2s1p }, - { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_0S1P, - 0x1c00, 0x3050, 0, 0, wch_ch382_0s1p }, - { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S1P, - 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p }, + { PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_1S1P), + .driver_data = wch_ch353_1s1p }, + { PCI_VDEVICE_SUB(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1P, 0x4348, 0x3253), + .driver_data = wch_ch353_2s1p }, + { PCI_VDEVICE_SUB(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_0S1P, 0x1c00, 0x3050), + .driver_data = wch_ch382_0s1p }, + { PCI_VDEVICE_SUB(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S1P, 0x1c00, 0x3250), + .driver_data = wch_ch382_2s1p }, /* BrainBoxes PX272/PX306 MIO card */ - { PCI_VENDOR_ID_INTASHIELD, 0x4100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p }, + { PCI_VDEVICE(INTASHIELD, 0x4100), + .driver_data = brainboxes_5s1p }, /* Sunix boards */ - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0100, 0, 0, sunix_4008a }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0101, 0, 0, sunix_5069a }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0102, 0, 0, sunix_5079a }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0104, 0, 0, sunix_5099a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0100), + .driver_data = sunix_4008a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0101), + .driver_data = sunix_5069a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0102), + .driver_data = sunix_5079a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0104), + .driver_data = sunix_5099a }, /* Brainboxes UC-203 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0bc1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0bc2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0bc1), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0bc2), + .driver_data = brainboxes_uc257 }, /* Brainboxes UC-257 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0861, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0862, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0863, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0861), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0862), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0863), + .driver_data = brainboxes_uc257 }, /* Brainboxes UC-414 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0e61, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc414 }, + { PCI_VDEVICE(INTASHIELD, 0x0e61), + .driver_data = brainboxes_uc414 }, /* Brainboxes UC-475 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0981, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0982, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0981), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0982), + .driver_data = brainboxes_uc257 }, /* Brainboxes IS-300/IS-500 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0da0, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_is300 }, + { PCI_VDEVICE(INTASHIELD, 0x0da0), + .driver_data = brainboxes_is300 }, /* Brainboxes PX-263/PX-295 */ - { PCI_VENDOR_ID_INTASHIELD, 0x402c, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px263 }, + { PCI_VDEVICE(INTASHIELD, 0x402c), + .driver_data = brainboxes_px263 }, - { 0, } /* terminate list */ + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl); diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index 7f5326925ed5..8d2d9d01cfd2 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -644,6 +644,15 @@ err_unlock: return ret; } +static void pci_epf_mhi_edma_flush_async(struct mhi_ep_cntrl *mhi_cntrl) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + + dmaengine_synchronize(epf_mhi->dma_chan_rx); + dmaengine_synchronize(epf_mhi->dma_chan_tx); + flush_workqueue(epf_mhi->dma_wq); +} + struct epf_dma_filter { struct device *dev; u32 dma_mask; @@ -812,6 +821,7 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf) mhi_cntrl->write_sync = pci_epf_mhi_edma_write; mhi_cntrl->read_async = pci_epf_mhi_edma_read_async; mhi_cntrl->write_async = pci_epf_mhi_edma_write_async; + mhi_cntrl->flush_async = pci_epf_mhi_edma_flush_async; } /* Register the MHI EP controller */ diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c index 7cb020dd3423..9591672b0511 100644 --- a/drivers/phy/motorola/phy-cpcap-usb.c +++ b/drivers/phy/motorola/phy-cpcap-usb.c @@ -717,3 +717,4 @@ MODULE_ALIAS("platform:cpcap_usb"); MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); MODULE_DESCRIPTION("CPCAP usb phy driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 032140bd6682..90a61d9db72c 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -826,3 +826,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); MODULE_ALIAS("platform:ab8500-btemp"); MODULE_DESCRIPTION("AB8500 battery temperature driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index bc6c070a564d..ff38254d06ef 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3748,3 +3748,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); MODULE_ALIAS("platform:ab8500-charger"); MODULE_DESCRIPTION("AB8500 charger management driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index cfcc05dfe28d..e6b5efdf5dc9 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -3262,3 +3262,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski"); MODULE_ALIAS("platform:ab8500-fg"); MODULE_DESCRIPTION("AB8500 Fuel Gauge driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index 474661ba7cf2..d9892b351621 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -418,3 +418,4 @@ module_platform_driver(axp20x_ac_power_driver); MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index 50ca8e110085..ee8701a6e907 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -1155,3 +1155,4 @@ module_platform_driver(axp20x_batt_driver); MODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs"); MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 29f105f9212d..e951b2b64b25 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -1077,3 +1077,4 @@ module_platform_driver(axp20x_usb_power_driver); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index aa04c54ff97e..68dabaf7a4f8 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -817,3 +817,4 @@ MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>"); MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>"); MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 1d2b8bb08564..808714c6ce95 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1202,3 +1202,4 @@ module_platform_driver(cpcap_battery_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); MODULE_DESCRIPTION("CPCAP PMIC Battery Driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 76e2d2e3f5f5..34a57dd5c3cf 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -973,3 +973,4 @@ MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); MODULE_DESCRIPTION("CPCAP Battery Charger Interface driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:cpcap-charger"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/da9150-charger.c b/drivers/power/supply/da9150-charger.c index 27f36ef5b88d..58449df6068c 100644 --- a/drivers/power/supply/da9150-charger.c +++ b/drivers/power/supply/da9150-charger.c @@ -644,3 +644,4 @@ module_platform_driver(da9150_charger_driver); MODULE_DESCRIPTION("Charger Driver for DA9150"); MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c index 6913179ebb84..185f55d2fd70 100644 --- a/drivers/power/supply/generic-adc-battery.c +++ b/drivers/power/supply/generic-adc-battery.c @@ -298,3 +298,4 @@ module_platform_driver(gab_driver); MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>"); MODULE_DESCRIPTION("generic battery driver using IIO"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index b111c7ce2be3..5be269f17bff 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -190,3 +190,4 @@ module_platform_driver(ingenic_battery_driver); MODULE_DESCRIPTION("Battery driver for Ingenic JZ47xx SoCs"); MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/intel_dc_ti_battery.c b/drivers/power/supply/intel_dc_ti_battery.c index 67a75281b0ac..cb6fa8d88b43 100644 --- a/drivers/power/supply/intel_dc_ti_battery.c +++ b/drivers/power/supply/intel_dc_ti_battery.c @@ -389,3 +389,4 @@ MODULE_ALIAS("platform:" DEV_NAME); MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); MODULE_DESCRIPTION("Intel Dollar Cove (TI) battery driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/lego_ev3_battery.c b/drivers/power/supply/lego_ev3_battery.c index 582644f4e8b3..1e53f11a8e2b 100644 --- a/drivers/power/supply/lego_ev3_battery.c +++ b/drivers/power/supply/lego_ev3_battery.c @@ -230,3 +230,4 @@ module_platform_driver(lego_ev3_battery_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Lechner <david@lechnology.com>"); MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index 1bc2c150fb91..b8b74efc9671 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -727,3 +727,4 @@ MODULE_DESCRIPTION("TI LP8788 Charger Driver"); MODULE_AUTHOR("Milo Kim"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:lp8788-charger"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 41744191a363..16e42de28436 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -655,3 +655,4 @@ module_i2c_driver(max17040_i2c_driver); MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/mp2629_charger.c b/drivers/power/supply/mp2629_charger.c index 04294359ed7d..0aace0040259 100644 --- a/drivers/power/supply/mp2629_charger.c +++ b/drivers/power/supply/mp2629_charger.c @@ -657,3 +657,4 @@ module_platform_driver(mp2629_charger_driver); MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>"); MODULE_DESCRIPTION("MP2629 Charger driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c index 9d388bc53098..29ce34fd3804 100644 --- a/drivers/power/supply/mt6370-charger.c +++ b/drivers/power/supply/mt6370-charger.c @@ -928,3 +928,4 @@ module_platform_driver(mt6370_chg_driver); MODULE_AUTHOR("ChiaEn Wu <chiaen_wu@richtek.com>"); MODULE_DESCRIPTION("MediaTek MT6370 Charger Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c index 7bda5cd77a75..67fdb4335338 100644 --- a/drivers/power/supply/qcom_smbx.c +++ b/drivers/power/supply/qcom_smbx.c @@ -1056,3 +1056,4 @@ module_platform_driver(qcom_spmi_smb); MODULE_AUTHOR("Casey Connolly <casey.connolly@linaro.org>"); MODULE_DESCRIPTION("Qualcomm SMB2 Charger Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c index 5963a55341ee..84b9d6248489 100644 --- a/drivers/power/supply/rn5t618_power.c +++ b/drivers/power/supply/rn5t618_power.c @@ -818,3 +818,4 @@ module_platform_driver(rn5t618_power_driver); MODULE_ALIAS("platform:rn5t618-power"); MODULE_DESCRIPTION("Power supply driver for RICOH RN5T618"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/rx51_battery.c b/drivers/power/supply/rx51_battery.c index b0220ec2d926..57266921dc8e 100644 --- a/drivers/power/supply/rx51_battery.c +++ b/drivers/power/supply/rx51_battery.c @@ -246,3 +246,4 @@ MODULE_ALIAS("platform:rx51-battery"); MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); MODULE_DESCRIPTION("Nokia RX-51 battery driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 9af0017fa88e..0999fc5bc662 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -1346,3 +1346,4 @@ module_platform_driver(sc27xx_fgu_driver); MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index c46b22ab8d99..e6c9b2b1ffce 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -1146,3 +1146,4 @@ MODULE_AUTHOR("Gražvydas Ignotas"); MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030_bci"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl4030_madc_battery.c b/drivers/power/supply/twl4030_madc_battery.c index a99b3ff26929..5e04b4f0a135 100644 --- a/drivers/power/supply/twl4030_madc_battery.c +++ b/drivers/power/supply/twl4030_madc_battery.c @@ -235,3 +235,4 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>"); MODULE_DESCRIPTION("twl4030_madc battery driver"); MODULE_ALIAS("platform:twl4030_madc_battery"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl6030_charger.c b/drivers/power/supply/twl6030_charger.c index 020512d0e306..e8ef221fa0f3 100644 --- a/drivers/power/supply/twl6030_charger.c +++ b/drivers/power/supply/twl6030_charger.c @@ -577,3 +577,4 @@ module_platform_driver(twl6030_charger_driver); MODULE_DESCRIPTION("TWL6030 Battery Charger Interface driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c index 0e32a2295990..30644d2d7c54 100644 --- a/drivers/staging/iio/adc/ad7816.c +++ b/drivers/staging/iio/adc/ad7816.c @@ -426,9 +426,9 @@ static const struct of_device_id ad7816_of_match[] = { MODULE_DEVICE_TABLE(of, ad7816_of_match); static const struct spi_device_id ad7816_id[] = { - { "ad7816", ID_AD7816 }, - { "ad7817", ID_AD7817 }, - { "ad7818", ID_AD7818 }, + { .name = "ad7816", .driver_data = ID_AD7816 }, + { .name = "ad7817", .driver_data = ID_AD7817 }, + { .name = "ad7818", .driver_data = ID_AD7818 }, { } }; diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c index 1debcc36c1af..459dd7416b1d 100644 --- a/drivers/staging/iio/addac/adt7316-spi.c +++ b/drivers/staging/iio/addac/adt7316-spi.c @@ -126,15 +126,14 @@ static int adt7316_spi_probe(struct spi_device *spi_dev) } static const struct spi_device_id adt7316_spi_id[] = { - { "adt7316", 0 }, - { "adt7317", 0 }, - { "adt7318", 0 }, - { "adt7516", 0 }, - { "adt7517", 0 }, - { "adt7519", 0 }, + { .name = "adt7316" }, + { .name = "adt7317" }, + { .name = "adt7318" }, + { .name = "adt7516" }, + { .name = "adt7517" }, + { .name = "adt7519" }, { } }; - MODULE_DEVICE_TABLE(spi, adt7316_spi_id); static const struct of_device_id adt7316_of_spi_match[] = { diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c index 59fb3bd26bc1..20f6052cc7fb 100644 --- a/drivers/staging/iio/addac/adt7316.c +++ b/drivers/staging/iio/addac/adt7316.c @@ -1814,11 +1814,8 @@ static int adt7316_setup_irq(struct iio_dev *indio_dev) NULL, adt7316_event_handler, irq_type | IRQF_ONESHOT, indio_dev->name, indio_dev); - if (ret) { - dev_err(&indio_dev->dev, "failed to request irq %d\n", - chip->bus.irq); + if (ret) return ret; - } if (irq_type & IRQF_TRIGGER_HIGH) chip->config1 |= ADT7316_INT_POLARITY; diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 60c33e10c46f..a78249812b26 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -252,22 +252,22 @@ error_ret: * see dds.h for further information */ -static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9832_write, AD9832_FREQ0HM); -static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9832_write, AD9832_FREQ1HM); -static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9832_write, AD9832_FREQ_SYM); +static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9832_write, AD9832_FREQ0HM); +static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9832_write, AD9832_FREQ1HM); +static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9832_write, AD9832_FREQ_SYM); static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ -static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9832_write, AD9832_PHASE0H); -static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9832_write, AD9832_PHASE1H); -static IIO_DEV_ATTR_PHASE(0, 2, 0200, NULL, ad9832_write, AD9832_PHASE2H); -static IIO_DEV_ATTR_PHASE(0, 3, 0200, NULL, ad9832_write, AD9832_PHASE3H); -static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, +static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9832_write, AD9832_PHASE0H); +static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9832_write, AD9832_PHASE1H); +static IIO_DEV_ATTR_PHASE(0200, 0, 2, NULL, ad9832_write, AD9832_PHASE2H); +static IIO_DEV_ATTR_PHASE(0200, 0, 3, NULL, ad9832_write, AD9832_PHASE3H); +static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL, ad9832_write, AD9832_PHASE_SYM); static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ -static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, +static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL, ad9832_write, AD9832_PINCTRL_EN); -static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, +static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL, ad9832_write, AD9832_OUTPUT_EN); static struct attribute *ad9832_attributes[] = { @@ -380,8 +380,8 @@ static const struct of_device_id ad9832_of_match[] = { MODULE_DEVICE_TABLE(of, ad9832_of_match); static const struct spi_device_id ad9832_id[] = { - {"ad9832", 0}, - {"ad9835", 0}, + { .name = "ad9832" }, + { .name = "ad9835" }, { } }; MODULE_DEVICE_TABLE(spi, ad9832_id); diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 33dfd723923c..0d20f796e0f0 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -314,21 +314,21 @@ static IIO_DEVICE_ATTR(out_altvoltage0_out1_wavetype_available, 0444, * see dds.h for further information */ -static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9834_write, AD9834_REG_FREQ0); -static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9834_write, AD9834_REG_FREQ1); -static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9834_write, AD9834_FSEL); +static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9834_write, AD9834_REG_FREQ0); +static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9834_write, AD9834_REG_FREQ1); +static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9834_write, AD9834_FSEL); static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ -static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9834_write, AD9834_REG_PHASE0); -static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9834_write, AD9834_REG_PHASE1); -static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, ad9834_write, AD9834_PSEL); +static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9834_write, AD9834_REG_PHASE0); +static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9834_write, AD9834_REG_PHASE1); +static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL, ad9834_write, AD9834_PSEL); static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ -static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, ad9834_write, AD9834_PIN_SW); -static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, ad9834_write, AD9834_RESET); -static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, 0200, NULL, ad9834_write, AD9834_OPBITEN); -static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); -static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); +static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL, ad9834_write, AD9834_PIN_SW); +static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL, ad9834_write, AD9834_RESET); +static IIO_DEV_ATTR_OUTY_ENABLE(0200, 0, 1, NULL, ad9834_write, AD9834_OPBITEN); +static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 0, ad9834_store_wavetype, 0); +static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 1, ad9834_store_wavetype, 1); static struct attribute *ad9834_attributes[] = { &iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr, @@ -464,10 +464,10 @@ static int ad9834_probe(struct spi_device *spi) } static const struct spi_device_id ad9834_id[] = { - {"ad9833", ID_AD9833}, - {"ad9834", ID_AD9834}, - {"ad9837", ID_AD9837}, - {"ad9838", ID_AD9838}, + { .name = "ad9833", .driver_data = ID_AD9833 }, + { .name = "ad9834", .driver_data = ID_AD9834 }, + { .name = "ad9837", .driver_data = ID_AD9837 }, + { .name = "ad9838", .driver_data = ID_AD9838 }, { } }; MODULE_DEVICE_TABLE(spi, ad9834_id); diff --git a/drivers/staging/iio/frequency/dds.h b/drivers/staging/iio/frequency/dds.h index 2ebe68eb7398..d5f0139b79f0 100644 --- a/drivers/staging/iio/frequency/dds.h +++ b/drivers/staging/iio/frequency/dds.h @@ -7,102 +7,102 @@ #ifndef IIO_DDS_H_ #define IIO_DDS_H_ -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY */ -#define IIO_DEV_ATTR_FREQ(_channel, _num, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_FREQ(_mode, _channel, _num, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequency##_num, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY_scale */ #define IIO_CONST_ATTR_FREQ_SCALE(_channel, _string) \ IIO_CONST_ATTR(out_altvoltage##_channel##_frequency_scale, _string) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol */ -#define IIO_DEV_ATTR_FREQSYMBOL(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_FREQSYMBOL(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequencysymbol, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_phaseY */ -#define IIO_DEV_ATTR_PHASE(_channel, _num, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_PHASE(_mode, _channel, _num, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phase##_num, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_phaseY_scale */ #define IIO_CONST_ATTR_PHASE_SCALE(_channel, _string) \ IIO_CONST_ATTR(out_altvoltage##_channel##_phase_scale, _string) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_phasesymbol */ -#define IIO_DEV_ATTR_PHASESYMBOL(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_PHASESYMBOL(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phasesymbol, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en */ -#define IIO_DEV_ATTR_PINCONTROL_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_en, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en */ -#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_frequency_en,\ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en */ -#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_phase_en, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_out_enable */ -#define IIO_DEV_ATTR_OUT_ENABLE(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_OUT_ENABLE(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out_enable, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_outY_enable */ -#define IIO_DEV_ATTR_OUTY_ENABLE(_channel, _output, \ - _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_OUTY_ENABLE(_mode, _channel, _output, \ + _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_enable,\ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype */ -#define IIO_DEV_ATTR_OUT_WAVETYPE(_channel, _output, _store, _addr) \ +#define IIO_DEV_ATTR_OUT_WAVETYPE(_mode, _channel, _output, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_wavetype,\ - 0200, NULL, _store, _addr) + _mode, NULL, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available */ diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c index d1b086737bcd..af72db6299cd 100644 --- a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c @@ -1066,3 +1066,4 @@ module_platform_driver(adc_tm5_driver); MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index f39ca0ddd17b..fb003ca96454 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -904,3 +904,4 @@ module_platform_driver(qpnp_tm_driver); MODULE_ALIAS("platform:spmi-temp-alarm"); MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/renesas/rzg3s_thermal.c b/drivers/thermal/renesas/rzg3s_thermal.c index e25e36c99a88..7ced8f76a0ec 100644 --- a/drivers/thermal/renesas/rzg3s_thermal.c +++ b/drivers/thermal/renesas/rzg3s_thermal.c @@ -270,3 +270,4 @@ module_platform_driver(rzg3s_thermal_driver); MODULE_DESCRIPTION("Renesas RZ/G3S Thermal Sensor Unit Driver"); MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index 7c844589b153..cfdb8e674dd2 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -228,3 +228,4 @@ module_platform_driver(gadc_thermal_driver); MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework with DT"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 1e4ade78ed84..f8fa20522660 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -150,7 +150,7 @@ static ssize_t portio_size_show(struct uio_port *port, char *buf) static ssize_t portio_porttype_show(struct uio_port *port, char *buf) { - const char *porttypes[] = {"none", "x86", "gpio", "other"}; + static const char * const porttypes[] = {"none", "x86", "gpio", "other"}; if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER)) return -EINVAL; @@ -1057,6 +1057,11 @@ int __uio_register_device(struct module *owner, err_request_irq: uio_dev_del_attributes(idev); err_uio_dev_add_attributes: + mutex_lock(&idev->info_lock); + idev->info = NULL; + mutex_unlock(&idev->info_lock); + wake_up_interruptible(&idev->wait); + kill_fasync(&idev->async_queue, SIGIO, POLL_HUP); device_del(&idev->dev); err_device_create: uio_free_minor(idev->minor); diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c index 338dd2aaabc8..08cb150fb12f 100644 --- a/drivers/uio/uio_fsl_elbc_gpcm.c +++ b/drivers/uio/uio_fsl_elbc_gpcm.c @@ -45,8 +45,7 @@ #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/uio_driver.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> +#include <linux/of.h> #include <asm/fsl_lbc.h> @@ -249,19 +248,12 @@ static int check_of_data(struct fsl_elbc_gpcm *priv, static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node, struct resource *res, u32 *reg_br, - u32 *reg_or, unsigned int *irq, char **name) + u32 *reg_or, char **name) { const char *dt_name; const char *type; int ret; - /* get the memory resource */ - ret = of_address_to_resource(node, 0, res); - if (ret) { - dev_err(priv->dev, "failed to get resource\n"); - return ret; - } - /* get the bank number */ ret = of_property_read_u32(node, "reg", &priv->bank); if (ret) { @@ -288,9 +280,6 @@ static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node, if (of_property_read_string(node, "device_type", &type) == 0) setup_periph(priv, type); - /* get optional irq value */ - *irq = irq_of_parse_and_map(node, 0); - /* sanity check device tree data */ ret = check_of_data(priv, res, *reg_br, *reg_or); if (ret) @@ -312,7 +301,8 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) struct fsl_elbc_gpcm *priv; struct uio_info *info; char *uio_name = NULL; - struct resource res; + struct resource *res; + void __iomem *base; unsigned int irq; u32 reg_br_cur; u32 reg_or_cur; @@ -323,6 +313,16 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) return -ENODEV; + /* map the memory resource */ + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + /* get optional irq value */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + /* allocate private data */ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -331,8 +331,8 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) priv->lbc = fsl_lbc_ctrl_dev->regs; /* get device tree data */ - ret = get_of_data(priv, node, &res, ®_br_new, ®_or_new, - &irq, &uio_name); + ret = get_of_data(priv, node, res, ®_br_new, ®_or_new, + &uio_name); if (ret) return ret; @@ -349,7 +349,7 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) if ((reg_br_cur & BR_V)) { if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM || (reg_br_cur & reg_or_cur & BR_BA) - != fsl_lbc_addr(res.start)) { + != fsl_lbc_addr(res->start)) { dev_err(priv->dev, "bank in use by another peripheral\n"); return -ENODEV; @@ -371,26 +371,18 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) /* configure the bank (force base address and GPCM) */ reg_br_new &= ~(BR_BA | BR_MSEL); - reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V; + reg_br_new |= fsl_lbc_addr(res->start) | BR_MS_GPCM | BR_V; out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new); out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new); - /* map the memory resource */ - info->mem[0].internal_addr = ioremap(res.start, resource_size(&res)); - if (!info->mem[0].internal_addr) { - dev_err(priv->dev, "failed to map chip region\n"); - return -ENODEV; - } - /* set all UIO data */ info->mem[0].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn", node); - if (!info->mem[0].name) { - ret = -ENODEV; - goto out_err3; - } + if (!info->mem[0].name) + return -ENODEV; - info->mem[0].addr = res.start; - info->mem[0].size = resource_size(&res); + info->mem[0].internal_addr = base; + info->mem[0].addr = res->start; + info->mem[0].size = resource_size(res); info->mem[0].memtype = UIO_MEM_PHYS; info->priv = priv; info->name = uio_name; @@ -421,16 +413,13 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) dev_info(priv->dev, "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n", - priv->name, (unsigned long long)res.start, priv->bank, + priv->name, (unsigned long long)res->start, priv->bank, irq ? : -1); return 0; out_err2: if (priv->shutdown) priv->shutdown(info, true); - -out_err3: - iounmap(info->mem[0].internal_addr); return ret; } @@ -443,7 +432,6 @@ static void uio_fsl_elbc_gpcm_remove(struct platform_device *pdev) uio_unregister_device(info); if (priv->shutdown) priv->shutdown(info, false); - iounmap(info->mem[0].internal_addr); } diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c index 12afc2fa1a0b..80087897f9b8 100644 --- a/drivers/uio/uio_sercos3.c +++ b/drivers/uio/uio_sercos3.c @@ -212,6 +212,7 @@ static const struct pci_device_id sercos3_pci_ids[] = { }, { 0, } }; +MODULE_DEVICE_TABLE(pci, sercos3_pci_ids); static struct pci_driver sercos3_pci_driver = { .name = "sercos3", diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index 0069e6f854d7..cbd11bc61274 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -310,6 +310,10 @@ static u8 ds2482_w1_triplet(void *data, u8 dbit) mutex_unlock(&pdev->access_lock); + /* On bus error, decode to 3 (no device responded) to abort the search */ + if (status < 0) + status = 3 << 5; + /* Decode the status */ return (status >> 5); } @@ -545,9 +549,17 @@ static const struct i2c_device_id ds2482_id[] = { }; MODULE_DEVICE_TABLE(i2c, ds2482_id); +static const struct of_device_id ds2482_of_match[] = { + { .compatible = "maxim,ds2482", }, + { .compatible = "maxim,ds2484", }, + { } +}; +MODULE_DEVICE_TABLE(of, ds2482_of_match); + static struct i2c_driver ds2482_driver = { .driver = { .name = "ds2482", + .of_match_table = ds2482_of_match, }, .probe = ds2482_probe, .remove = ds2482_remove, diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c index e53bc41bde3c..b638963d4b59 100644 --- a/drivers/w1/slaves/w1_ds28e17.c +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -389,6 +389,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, * another simple read in that case. */ if (msgs[i+1].flags & I2C_M_RECV_LEN) { + if (msgs[i+1].buf[0] > I2C_SMBUS_BLOCK_MAX) { + i = -EPROTO; + goto error; + } result = w1_f19_i2c_read(sl, msgs[i+1].addr, &(msgs[i+1].buf[1]), msgs[i+1].buf[0]); if (result < 0) { @@ -415,6 +419,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, * another simple read in that case. */ if (msgs[i].flags & I2C_M_RECV_LEN) { + if (msgs[i].buf[0] > I2C_SMBUS_BLOCK_MAX) { + i = -EPROTO; + goto error; + } result = w1_f19_i2c_read(sl, msgs[i].addr, &(msgs[i].buf[1]), diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 486f321eadc8..c16946642789 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -403,6 +403,11 @@ static int w1_atoreg_num(struct device *dev, const char *buf, size_t count, const char *error_msg = "bad slave string format, expecting " "ff-dddddddddddd\n"; + if (count < 3) { + dev_err(dev, "%s", error_msg); + return -EINVAL; + } + if (buf[2] != '-') { dev_err(dev, "%s", error_msg); return -EINVAL; |
