A similar fixup needs to be done for the userspace bits as well, but as
this fixup started only because in /proc/kallsyms we don't have the end
address nor the function size, it appeared here first.
Cc: Frédéric Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Mike Galbraith <efa...@gmx.de>
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
---
tools/perf/util/symbol.c | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e3eebdd..582ce72 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -396,8 +396,7 @@ static inline int elf_sym__is_function(const GElf_Sym *sym)
{
return elf_sym__type(sym) == STT_FUNC &&
sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF &&
- sym->st_size != 0;
+ sym->st_shndx != SHN_UNDEF;
}
static inline int elf_sym__is_label(const GElf_Sym *sym)
--
1.6.2.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Cc: Frederic Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Mike Galbraith <efa...@gmx.de>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Jesse Brandeburg <jesse.br...@gmail.com>
Suggested-by: Jesse Brandeburg <jesse.br...@gmail.com>
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
---
tools/perf/Makefile | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 5a42996..e714bd1 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -895,6 +895,7 @@ ifneq (,$X)
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';)
endif
endif
+ $(MAKE) -C Documentation install
install-doc:
$(MAKE) -C Documentation install
This is done by creating multiple 'struct map' instances for address
ranges backed by DSOs that have just the symbols for that range and a
name that is derived from the ELF section name.o
Now it is possible to ask for just the symbols in some particular kernel
section:
$ perf report -m --vmlinux ../build/tip-recvmmsg/vmlinux \
--dsos [kernel].vsyscall_fn | head -15
52.73% Xorg [.] vread_hpet
18.61% firefox [.] vread_hpet
14.50% npviewer.bin [.] vread_hpet
6.83% compiz [.] vread_hpet
5.73% glxgears [.] vread_hpet
0.63% java [.] vread_hpet
0.30% gnome-terminal [.] vread_hpet
0.23% perf [.] vread_hpet
0.18% xchat [.] vread_hpet
$
Now we don't have to first lookup the list of modules and then, if it
fails, vmlinux symbols, its just a simple lookup for the map then the
symbols, just like for threads.
Reports generated using /proc/kallsyms and --vmlinux should provide the
same results, modulo the DSO name for sections other than ".text".
But they don't right now because things like:
ffffffff81011c20-ffffffff81012068 system_call
ffffffff81011c30-ffffffff81011c9b system_call_after_swapgs
ffffffff81011c9c-ffffffff81011cb6 system_call_fastpath
ffffffff81011cb7-ffffffff81011cbb ret_from_sys_call
I.e. overlapping symbols, again some ASM special case that we have to
fixup.
Cc: Frederic Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Mike Galbraith <efa...@gmx.de>
Cc: Paul Mackerras <pau...@samba.org>
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
---
tools/perf/util/symbol.c | 288 ++++++++++++++++++++++++++++++++--------------
1 files changed, 201 insertions(+), 87 deletions(-)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index a6887f9..faa84f5 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -26,27 +26,35 @@ enum dso_origin {
static void dsos__add(struct dso *dso);
static struct dso *dsos__find(const char *name);
+static struct map *map__new2(u64 start, struct dso *dso);
+static void kernel_maps__insert(struct map *map);
static struct rb_root kernel_maps;
-static void dso__set_symbols_end(struct dso *self)
+static void dso__fixup_sym_end(struct dso *self)
{
struct rb_node *nd, *prevnd = rb_first(&self->syms);
+ struct symbol *curr, *prev;
if (prevnd == NULL)
return;
+ curr = rb_entry(prevnd, struct symbol, rb_node);
+
for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
- struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node),
- *curr = rb_entry(nd, struct symbol, rb_node);
+ prev = curr;
+ curr = rb_entry(nd, struct symbol, rb_node);
if (prev->end == prev->start)
prev->end = curr->start - 1;
- prevnd = nd;
}
+
+ /* Last entry */
+ if (curr->end == curr->start)
+ curr->end = roundup(curr->start, 4096);
}
-static void kernel_maps__fixup_sym_end(void)
+static void kernel_maps__fixup_end(void)
{
struct map *prev, *curr;
struct rb_node *nd, *prevnd = rb_first(&kernel_maps);
@@ -55,13 +63,17 @@ static void kernel_maps__fixup_sym_end(void)
return;
curr = rb_entry(prevnd, struct map, rb_node);
- dso__set_symbols_end(curr->dso);
for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
prev = curr;
curr = rb_entry(nd, struct map, rb_node);
prev->end = curr->start - 1;
- dso__set_symbols_end(curr->dso);
+ }
+
+ nd = rb_last(&curr->dso->syms);
+ if (nd) {
+ struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ curr->end = sym->end;
}
}
@@ -200,13 +212,16 @@ size_t dso__fprintf(struct dso *self, FILE *fp)
return ret;
}
-static int maps__load_kallsyms(symbol_filter_t filter, int use_modules, int v)
+/*
+ * Loads the function entries in /proc/kallsyms into kernel_map->dso,
+ * so that we can in the next step set the symbol ->end address and then
+ * call kernel_maps__split_kallsyms.
+ */
+static int kernel_maps__load_all_kallsyms(int v)
{
- struct map *map = kernel_map;
char *line = NULL;
size_t n;
FILE *file = fopen("/proc/kallsyms", "r");
- int count = 0;
if (file == NULL)
goto out_failure;
@@ -216,7 +231,7 @@ static int maps__load_kallsyms(symbol_filter_t filter, int use_modules, int v)
struct symbol *sym;
int line_len, len;
char symbol_type;
- char *module, *symbol_name;
+ char *symbol_name;
line_len = getline(&line, &n, file);
if (line_len < 0)
@@ -241,20 +256,55 @@ static int maps__load_kallsyms(symbol_filter_t filter, int use_modules, int v)
continue;
symbol_name = line + len + 2;
- module = strchr(symbol_name, '\t');
- if (module) {
- char *module_name_end;
+ /*
+ * Will fix up the end later, when we have all symbols sorted.
+ */
+ sym = symbol__new(start, 0, symbol_name,
+ kernel_map->dso->sym_priv_size, v);
+ if (sym == NULL)
+ goto out_delete_line;
+
+ dso__insert_symbol(kernel_map->dso, sym);
+ }
+
+ free(line);
+ fclose(file);
+
+ return 0;
+
+out_delete_line:
+ free(line);
+out_failure:
+ return -1;
+}
+
+/*
+ * Split the symbols into maps, making sure there are no overlaps, i.e. the
+ * kernel range is broken in several maps, named [kernel].N, as we don't have
+ * the original ELF section names vmlinux have.
+ */
+static int kernel_maps__split_kallsyms(symbol_filter_t filter, int use_modules)
+{
+ struct map *map = kernel_map;
+ struct symbol *pos;
+ int count = 0;
+ struct rb_node *next = rb_first(&kernel_map->dso->syms);
+ int kernel_range = 0;
+
+ while (next) {
+ char *module;
+
+ pos = rb_entry(next, struct symbol, rb_node);
+ next = rb_next(&pos->rb_node);
+
+ module = strchr(pos->name, '\t');
+ if (module) {
if (!use_modules)
- continue;
- *module = '\0';
- module = strchr(module + 1, '[');
- if (!module)
- continue;
- module_name_end = strchr(module + 1, ']');
- if (!module_name_end)
- continue;
- *(module_name_end + 1) = '\0';
+ goto delete_symbol;
+
+ *module++ = '\0';
+
if (strcmp(map->dso->name, module)) {
map = kernel_maps__find_by_dso_name(module);
if (!map) {
@@ -263,50 +313,77 @@ static int maps__load_kallsyms(symbol_filter_t filter, int use_modules, int v)
return -1;
}
}
- start = map->map_ip(map, start);
- } else
- map = kernel_map;
- /*
- * Well fix up the end later, when we have all sorted.
- */
- sym = symbol__new(start, 0, symbol_name,
- map->dso->sym_priv_size, v);
+ /*
+ * So that we look just like we get from .ko files,
+ * i.e. not prelinked, relative to map->start.
+ */
+ pos->start = map->map_ip(map, pos->start);
+ pos->end = map->map_ip(map, pos->end);
+ } else if (map != kernel_map) {
+ char dso_name[PATH_MAX];
+ struct dso *dso;
+
+ snprintf(dso_name, sizeof(dso_name), "[kernel].%d",
+ kernel_range++);
+
+ dso = dso__new(dso_name,
+ kernel_map->dso->sym_priv_size);
+ if (dso == NULL)
+ return -1;
+
+ map = map__new2(pos->start, dso);
+ if (map == NULL) {
+ dso__delete(dso);
+ return -1;
+ }
- if (sym == NULL)
- goto out_delete_line;
+ map->map_ip = vdso__map_ip;
+ kernel_maps__insert(map);
+ ++kernel_range;
+ }
- if (filter && filter(map, sym))
- symbol__delete(sym, map->dso->sym_priv_size);
- else {
- dso__insert_symbol(map->dso, sym);
+ if (filter && filter(map, pos)) {
+delete_symbol:
+ rb_erase(&pos->rb_node, &kernel_map->dso->syms);
+ symbol__delete(pos, kernel_map->dso->sym_priv_size);
+ } else {
+ if (map != kernel_map) {
+ rb_erase(&pos->rb_node, &kernel_map->dso->syms);
+ dso__insert_symbol(map->dso, pos);
+ }
count++;
}
}
- free(line);
- fclose(file);
-
return count;
+}
-out_delete_line:
- free(line);
-out_failure:
- return -1;
+
+static int kernel_maps__load_kallsyms(symbol_filter_t filter,
+ int use_modules, int v)
+{
+ if (kernel_maps__load_all_kallsyms(v))
+ return -1;
+
+ dso__fixup_sym_end(kernel_map->dso);
+
+ return kernel_maps__split_kallsyms(filter, use_modules);
}
-static size_t kernel_maps__fprintf(FILE *fp)
+static size_t kernel_maps__fprintf(FILE *fp, int v)
{
size_t printed = fprintf(stderr, "Kernel maps:\n");
struct rb_node *nd;
- printed += map__fprintf(kernel_map, fp);
- printed += dso__fprintf(kernel_map->dso, fp);
-
for (nd = rb_first(&kernel_maps); nd; nd = rb_next(nd)) {
struct map *pos = rb_entry(nd, struct map, rb_node);
+ printed += fprintf(fp, "Map:");
printed += map__fprintf(pos, fp);
- printed += dso__fprintf(pos->dso, fp);
+ if (v > 1) {
+ printed += dso__fprintf(pos->dso, fp);
+ printed += fprintf(fp, "--\n");
+ }
}
return printed + fprintf(stderr, "END kernel maps\n");
@@ -594,6 +671,9 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
int fd, symbol_filter_t filter, int kernel,
int kmodule, int v)
{
+ struct map *curr_map = map;
+ struct dso *curr_dso = self;
+ size_t dso_name_len = strlen(self->short_name);
Elf_Data *symstrs, *secstrs;
uint32_t nr_syms;
int err = -1;
@@ -660,10 +740,9 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
struct symbol *f;
const char *elf_name;
- char *demangled;
+ char *demangled = NULL;
int is_label = elf_sym__is_label(&sym);
const char *section_name;
- u64 sh_offset = 0;
if (!is_label && !elf_sym__is_function(&sym))
continue;
@@ -677,14 +756,51 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
if (is_label && !elf_sec__is_text(&shdr, secstrs))
continue;
+ elf_name = elf_sym__name(&sym, symstrs);
section_name = elf_sec__name(&shdr, secstrs);
- if ((kernel || kmodule)) {
- if (strstr(section_name, ".init"))
- sh_offset = shdr.sh_offset;
+ if (kernel || kmodule) {
+ char dso_name[PATH_MAX];
+
+ if (strcmp(section_name,
+ curr_dso->short_name + dso_name_len) == 0)
+ goto new_symbol;
+
+ if (strcmp(section_name, ".text") == 0) {
+ curr_map = map;
+ curr_dso = self;
+ goto new_symbol;
+ }
+
+ snprintf(dso_name, sizeof(dso_name),
+ "%s%s", self->short_name, section_name);
+
+ curr_map = kernel_maps__find_by_dso_name(dso_name);
+ if (curr_map == NULL) {
+ u64 start = sym.st_value;
+
+ if (kmodule)
+ start += map->start + shdr.sh_offset;
+
+ curr_dso = dso__new(dso_name, self->sym_priv_size);
+ if (curr_dso == NULL)
+ goto out_elf_end;
+ curr_map = map__new2(start, curr_dso);
+ if (curr_map == NULL) {
+ dso__delete(curr_dso);
+ goto out_elf_end;
+ }
+ curr_map->map_ip = vdso__map_ip;
+ curr_dso->origin = DSO__ORIG_KERNEL;
+ kernel_maps__insert(curr_map);
+ dsos__add(curr_dso);
+ } else
+ curr_dso = curr_map->dso;
+
+ goto new_symbol;
}
- if (self->adjust_symbols) {
+ if (curr_dso->adjust_symbols) {
if (v > 2)
printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n",
(u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset);
@@ -696,25 +812,29 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
* DWARF DW_compile_unit has this, but we don't always have access
* to it...
*/
- elf_name = elf_sym__name(&sym, symstrs);
demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
if (demangled != NULL)
elf_name = demangled;
-
- f = symbol__new(sym.st_value + sh_offset, sym.st_size, elf_name,
- self->sym_priv_size, v);
+new_symbol:
+ f = symbol__new(sym.st_value, sym.st_size, elf_name,
+ curr_dso->sym_priv_size, v);
free(demangled);
if (!f)
goto out_elf_end;
- if (filter && filter(map, f))
- symbol__delete(f, self->sym_priv_size);
+ if (filter && filter(curr_map, f))
+ symbol__delete(f, curr_dso->sym_priv_size);
else {
- dso__insert_symbol(self, f);
+ dso__insert_symbol(curr_dso, f);
nr++;
}
}
+ /*
+ * For misannotated, zeroed, ASM function sizes.
+ */
+ if (nr > 0)
+ dso__fixup_sym_end(self);
err = nr;
out_elf_end:
elf_end(elf);
@@ -883,27 +1003,17 @@ static void kernel_maps__insert(struct map *map)
struct symbol *kernel_maps__find_symbol(u64 ip, struct map **mapp)
{
- /*
- * We can't have kernel_map in kernel_maps because it spans an address
- * space that includes the modules. The right way to fix this is to
- * create several maps, so that we don't have overlapping ranges with
- * modules. For now lets look first on the kernel dso.
- */
struct map *map = maps__find(&kernel_maps, ip);
- struct symbol *sym;
+
+ if (mapp)
+ *mapp = map;
if (map) {
ip = map->map_ip(map, ip);
- sym = map->dso->find_symbol(map->dso, ip);
- } else {
- map = kernel_map;
- sym = map->dso->find_symbol(map->dso, ip);
+ return map->dso->find_symbol(map->dso, ip);
}
- if (mapp)
- *mapp = map;
-
- return sym;
+ return NULL;
}
struct map *kernel_maps__find_by_dso_name(const char *name)
@@ -994,6 +1104,14 @@ static int dsos__load_modules_sym_dir(char *dirname,
last = rb_last(&map->dso->syms);
if (last) {
struct symbol *sym;
+ /*
+ * We do this here as well, even having the
+ * symbol size found in the symtab because
+ * misannotated ASM symbols may have the size
+ * set to zero.
+ */
+ dso__fixup_sym_end(map->dso);
+
sym = rb_entry(last, struct symbol, rb_node);
map->end = map->start + sym->end;
}
@@ -1163,17 +1281,11 @@ int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size,
}
if (err <= 0)
- err = maps__load_kallsyms(filter, use_modules, v);
+ err = kernel_maps__load_kallsyms(filter, use_modules, v);
if (err > 0) {
struct rb_node *node = rb_first(&dso->syms);
struct symbol *sym = rb_entry(node, struct symbol, rb_node);
- /*
- * Now that we have all sorted out, just set the ->end of all
- * symbols that still don't have it.
- */
- dso__set_symbols_end(dso);
- kernel_maps__fixup_sym_end();
kernel_map->start = sym->start;
node = rb_last(&dso->syms);
@@ -1181,14 +1293,16 @@ int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size,
kernel_map->end = sym->end;
dso->origin = DSO__ORIG_KERNEL;
+ kernel_maps__insert(kernel_map);
/*
- * XXX See kernel_maps__find_symbol comment
- * kernel_maps__insert(kernel_map)
+ * Now that we have all sorted out, just set the ->end of all
+ * maps:
*/
+ kernel_maps__fixup_end();
dsos__add(dso);
if (v > 0)
- kernel_maps__fprintf(stderr);
+ kernel_maps__fprintf(stderr, v);
}
return err;
> So that we get matching docs when installing a new version of the tools.
>
> Cc: Frederic Weisbecker <fwei...@gmail.com>
> Cc: Peter Zijlstra <pet...@infradead.org>
> Cc: Paul Mackerras <pau...@samba.org>
> Cc: Mike Galbraith <efa...@gmx.de>
> Cc: Paul Mackerras <pau...@samba.org>
> Cc: Jesse Brandeburg <jesse.br...@gmail.com>
> Suggested-by: Jesse Brandeburg <jesse.br...@gmail.com>
> Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
> ---
> tools/perf/Makefile | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/tools/perf/Makefile b/tools/perf/Makefile
> index 5a42996..e714bd1 100644
> --- a/tools/perf/Makefile
> +++ b/tools/perf/Makefile
> @@ -895,6 +895,7 @@ ifneq (,$X)
> $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';)
> endif
> endif
> + $(MAKE) -C Documentation install
>
> install-doc:
> $(MAKE) -C Documentation install
hm, i didnt want to do that because many systems dont have asciidoc and
xmlto installed by default.
We can still do it, if you can auto-detect these two new tools and emit
a warning (and dont build+install) documentation if they are missing -
instead of failing the build.
Ingo
perf tools: Improve kernel/modules symbol lookup
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
Cc: Frederic Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Mike Galbraith <efa...@gmx.de>
LKML-Reference: <1254934136-8503-1-...@redhat.com>
Signed-off-by: Ingo Molnar <mi...@elte.hu>
Cc: Frederic Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Mike Galbraith <efa...@gmx.de>
Cc: Paul Mackerras <pau...@samba.org>
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
---
tools/perf/builtin-annotate.c | 18 +++++++-----------
tools/perf/builtin-report.c | 17 +++++++----------
tools/perf/builtin-sched.c | 15 ++++++---------
tools/perf/builtin-trace.c | 9 +++------
tools/perf/util/thread.c | 40 ++++++++++++++++++----------------------
tools/perf/util/thread.h | 12 ++++--------
6 files changed, 45 insertions(+), 66 deletions(-)
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 8c84320..b64029f 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -37,10 +37,6 @@ static int print_line;
static unsigned long page_size;
static unsigned long mmap_window = 32;
-static struct rb_root threads;
-static struct thread *last_match;
-
-
struct sym_ext {
struct rb_node node;
double percent;
@@ -101,7 +97,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
struct map *map = NULL;
struct symbol *sym = NULL;
- thread = threads__findnew(event->ip.pid, &threads, &last_match);
+ thread = threads__findnew(event->ip.pid);
dump_printf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
(void *)(offset + head),
@@ -169,7 +165,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
struct thread *thread;
struct map *map = map__new(&event->mmap, NULL, 0);
- thread = threads__findnew(event->mmap.pid, &threads, &last_match);
+ thread = threads__findnew(event->mmap.pid);
dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n",
(void *)(offset + head),
@@ -196,7 +192,7 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread;
- thread = threads__findnew(event->comm.pid, &threads, &last_match);
+ thread = threads__findnew(event->comm.pid);
dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
@@ -218,8 +214,8 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head)
struct thread *thread;
struct thread *parent;
- thread = threads__findnew(event->fork.pid, &threads, &last_match);
- parent = threads__findnew(event->fork.ppid, &threads, &last_match);
+ thread = threads__findnew(event->fork.pid);
+ parent = threads__findnew(event->fork.ppid);
dump_printf("%p [%p]: PERF_RECORD_FORK: %d:%d\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
@@ -558,7 +554,7 @@ static int __cmd_annotate(void)
uint32_t size;
char *buf;
- register_idle_thread(&threads, &last_match);
+ register_idle_thread();
input = open(input_name, O_RDONLY);
if (input < 0) {
@@ -659,7 +655,7 @@ more:
return 0;
if (verbose > 3)
- threads__fprintf(stdout, &threads);
+ threads__fprintf(stdout);
if (verbose > 2)
dsos__fprintf(stdout);
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index f57a23b..0a3b014 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -55,9 +55,6 @@ static char callchain_default_opt[] = "fractal,0.5";
static char *cwd;
static int cwdlen;
-static struct rb_root threads;
-static struct thread *last_match;
-
static struct perf_header *header;
static u64 sample_type;
@@ -601,7 +598,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
struct ip_callchain *chain = NULL;
int cpumode;
- thread = threads__findnew(event->ip.pid, &threads, &last_match);
+ thread = threads__findnew(event->ip.pid);
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
@@ -688,7 +685,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
struct thread *thread;
struct map *map = map__new(&event->mmap, cwd, cwdlen);
- thread = threads__findnew(event->mmap.pid, &threads, &last_match);
+ thread = threads__findnew(event->mmap.pid);
dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
(void *)(offset + head),
@@ -716,7 +713,7 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread;
- thread = threads__findnew(event->comm.pid, &threads, &last_match);
+ thread = threads__findnew(event->comm.pid);
dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
(void *)(offset + head),
@@ -739,8 +736,8 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head)
struct thread *thread;
struct thread *parent;
- thread = threads__findnew(event->fork.pid, &threads, &last_match);
- parent = threads__findnew(event->fork.ppid, &threads, &last_match);
+ thread = threads__findnew(event->fork.pid);
+ parent = threads__findnew(event->fork.ppid);
dump_printf("%p [%p]: PERF_RECORD_%s: (%d:%d):(%d:%d)\n",
(void *)(offset + head),
@@ -857,7 +854,7 @@ static int __cmd_report(void)
struct thread *idle;
int ret;
- idle = register_idle_thread(&threads, &last_match);
+ idle = register_idle_thread();
thread__comm_adjust(idle);
if (show_threads)
@@ -881,7 +878,7 @@ static int __cmd_report(void)
return 0;
if (verbose > 3)
- threads__fprintf(stdout, &threads);
+ threads__fprintf(stdout);
if (verbose > 2)
dsos__fprintf(stdout);
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 25b91e7..d66b8e2 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -24,9 +24,6 @@ static char const *input_name = "perf.data";
static unsigned long total_comm = 0;
-static struct rb_root threads;
-static struct thread *last_match;
-
static struct perf_header *header;
static u64 sample_type;
@@ -638,7 +635,7 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread;
- thread = threads__findnew(event->comm.pid, &threads, &last_match);
+ thread = threads__findnew(event->comm.pid);
dump_printf("%p [%p]: perf_event_comm: %s:%d\n",
(void *)(offset + head),
@@ -1039,7 +1036,7 @@ threads__findnew_from_ctx(u32 pid, struct trace_switch_event *switch_event)
{
struct thread *th;
- th = threads__findnew_nocomm(pid, &threads, &last_match);
+ th = threads__findnew_nocomm(pid);
if (th->comm)
return th;
@@ -1055,7 +1052,7 @@ threads__findnew_from_wakeup(struct trace_wakeup_event *wakeup_event)
{
struct thread *th;
- th = threads__findnew_nocomm(wakeup_event->pid, &threads, &last_match);
+ th = threads__findnew_nocomm(wakeup_event->pid);
if (th->comm)
return th;
@@ -1130,7 +1127,7 @@ latency_runtime_event(struct trace_runtime_event *runtime_event,
BUG_ON(cpu >= MAX_CPUS || cpu < 0);
- thread = threads__findnew(runtime_event->pid, &threads, &last_match);
+ thread = threads__findnew(runtime_event->pid);
atoms = thread_atoms_search(&atom_root, thread, &cmp_pid);
if (!atoms) {
thread_atoms_insert(thread);
@@ -1589,7 +1586,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
if (!(sample_type & PERF_SAMPLE_RAW))
return 0;
- thread = threads__findnew(event->ip.pid, &threads, &last_match);
+ thread = threads__findnew(event->ip.pid);
if (sample_type & PERF_SAMPLE_TIME) {
timestamp = *(u64 *)more_data;
@@ -1662,7 +1659,7 @@ static struct perf_file_handler file_handler = {
static int read_events(void)
{
- register_idle_thread(&threads, &last_match);
+ register_idle_thread();
register_perf_file_handler(&file_handler);
return mmap_dispatch_perf_file(&header, input_name, 0, 0, &cwdlen, &cwd);
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index fb3f3c2..7dcd8f9 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -19,9 +19,6 @@ static char const *input_name = "perf.data";
static unsigned long total = 0;
static unsigned long total_comm = 0;
-static struct rb_root threads;
-static struct thread *last_match;
-
static struct perf_header *header;
static u64 sample_type;
@@ -34,7 +31,7 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread;
- thread = threads__findnew(event->comm.pid, &threads, &last_match);
+ thread = threads__findnew(event->comm.pid);
dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
(void *)(offset + head),
@@ -61,7 +58,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
u64 period = 1;
void *more_data = event->ip.__more_data;
- thread = threads__findnew(event->ip.pid, &threads, &last_match);
+ thread = threads__findnew(event->ip.pid);
if (sample_type & PERF_SAMPLE_TIME) {
timestamp = *(u64 *)more_data;
@@ -135,7 +132,7 @@ static struct perf_file_handler file_handler = {
static int __cmd_trace(void)
{
- register_idle_thread(&threads, &last_match);
+ register_idle_thread();
register_perf_file_handler(&file_handler);
return mmap_dispatch_perf_file(&header, input_name, 0, 0, &cwdlen, &cwd);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 8bd5ca2..0b0ea59 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -6,6 +6,9 @@
#include "util.h"
#include "debug.h"
+static struct rb_root threads;
+static struct thread *last_match;
+
static struct thread *thread__new(pid_t pid, int set_comm)
{
struct thread *self = calloc(1, sizeof(*self));
@@ -52,12 +55,9 @@ static size_t thread__fprintf(struct thread *self, FILE *fp)
return ret;
}
-static struct thread *
-__threads__findnew(pid_t pid, struct rb_root *threads,
- struct thread **last_match,
- int set_comm)
+static struct thread *__threads__findnew(pid_t pid, int set_comm)
{
- struct rb_node **p = &threads->rb_node;
+ struct rb_node **p = &threads.rb_node;
struct rb_node *parent = NULL;
struct thread *th;
@@ -66,15 +66,15 @@ __threads__findnew(pid_t pid, struct rb_root *threads,
* so most of the time we dont have to look up
* the full rbtree:
*/
- if (*last_match && (*last_match)->pid == pid)
- return *last_match;
+ if (last_match && last_match->pid == pid)
+ return last_match;
while (*p != NULL) {
parent = *p;
th = rb_entry(parent, struct thread, rb_node);
if (th->pid == pid) {
- *last_match = th;
+ last_match = th;
return th;
}
@@ -88,30 +88,26 @@ __threads__findnew(pid_t pid, struct rb_root *threads,
if (th != NULL) {
rb_link_node(&th->rb_node, parent, p);
- rb_insert_color(&th->rb_node, threads);
- *last_match = th;
+ rb_insert_color(&th->rb_node, &threads);
+ last_match = th;
}
return th;
}
-struct thread *
-threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)
+struct thread *threads__findnew(pid_t pid)
{
- return __threads__findnew(pid, threads, last_match, 1);
+ return __threads__findnew(pid, 1);
}
-struct thread *
-threads__findnew_nocomm(pid_t pid, struct rb_root *threads,
- struct thread **last_match)
+struct thread *threads__findnew_nocomm(pid_t pid)
{
- return __threads__findnew(pid, threads, last_match, 0);
+ return __threads__findnew(pid, 0);
}
-struct thread *
-register_idle_thread(struct rb_root *threads, struct thread **last_match)
+struct thread *register_idle_thread(void)
{
- struct thread *thread = threads__findnew(0, threads, last_match);
+ struct thread *thread = threads__findnew(0);
if (!thread || thread__set_comm(thread, "swapper")) {
fprintf(stderr, "problem inserting idle task.\n");
@@ -215,12 +211,12 @@ int thread__fork(struct thread *self, struct thread *parent)
return 0;
}
-size_t threads__fprintf(FILE *fp, struct rb_root *threads)
+size_t threads__fprintf(FILE *fp)
{
size_t ret = 0;
struct rb_node *nd;
- for (nd = rb_first(threads); nd; nd = rb_next(nd)) {
+ for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
struct thread *pos = rb_entry(nd, struct thread, rb_node);
ret += thread__fprintf(pos, fp);
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 75bc843..40ace42 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -15,16 +15,12 @@ struct thread {
};
int thread__set_comm(struct thread *self, const char *comm);
-struct thread *
-threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match);
-struct thread *
-threads__findnew_nocomm(pid_t pid, struct rb_root *threads,
- struct thread **last_match);
-struct thread *
-register_idle_thread(struct rb_root *threads, struct thread **last_match);
+struct thread *threads__findnew(pid_t pid);
+struct thread *threads__findnew_nocomm(pid_t pid);
+struct thread *register_idle_thread(void);
void thread__insert_map(struct thread *self, struct map *map);
int thread__fork(struct thread *self, struct thread *parent);
-size_t threads__fprintf(FILE *fp, struct rb_root *threads);
+size_t threads__fprintf(FILE *fp);
void maps__insert(struct rb_root *maps, struct map *map);
struct map *maps__find(struct rb_root *maps, u64 ip);
--
1.6.2.5
Cc: Steven Rostedt <sros...@redhat.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Frederic Weisbecker <fwei...@gmail.com>
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
---
tools/perf/util/trace-event-parse.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index 4b61b49..0b8a01b 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -286,7 +286,7 @@ void parse_ftrace_printk(char *file, unsigned int size __unused)
char *line;
char *next = NULL;
char *addr_str;
- char *fmt;
+ char *fmt = NULL;
int i;
line = strtok_r(file, "\n", &next);
--
1.5.5.1
> cc1: warnings being treated as errors
> util/trace-event-parse.c: In function ‘parse_ftrace_printk’:
> util/trace-event-parse.c:289: warning: ‘fmt’ may be used uninitialized in this function
>
> Cc: Steven Rostedt <sros...@redhat.com>
> Cc: Peter Zijlstra <pet...@infradead.org>
> Cc: Frederic Weisbecker <fwei...@gmail.com>
> Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
> ---
> tools/perf/util/trace-event-parse.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
> index 4b61b49..0b8a01b 100644
> --- a/tools/perf/util/trace-event-parse.c
> +++ b/tools/perf/util/trace-event-parse.c
> @@ -286,7 +286,7 @@ void parse_ftrace_printk(char *file, unsigned int size __unused)
> char *line;
> char *next = NULL;
> char *addr_str;
> - char *fmt;
> + char *fmt = NULL;
> int i;
>
> line = strtok_r(file, "\n", &next);
Actually, this might be a real bug that GCC pointed out: what makes sure
that strtok_r() does not return a NULL (no more tokens) and hence fmt
remains NULL (crashing the rest of the parser)?
Ingo
Ug, yeah that's a bug. That was one of the later changes I did at night.
I'll go look at it and see how it should really be.
Thanks,
-- Steve
Now we pass a callback that will receive the synthesized event and then write
it to the output file in 'perf record' and in the upcoming patch for 'perf top'
we will just immediatelly create the in memory representation of threads and
maps.
Cc: Frederic Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Mike Galbraith <efa...@gmx.de>
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
---
tools/perf/Makefile | 1 +
tools/perf/builtin-record.c | 181 +++----------------------------------------
tools/perf/util/event.c | 177 ++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/event.h | 3 +
4 files changed, 191 insertions(+), 171 deletions(-)
create mode 100644 tools/perf/util/event.c
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 39a84be..800783d 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -380,6 +380,7 @@ LIB_OBJS += util/alias.o
LIB_OBJS += util/config.o
LIB_OBJS += util/ctype.o
LIB_OBJS += util/environment.o
+LIB_OBJS += util/event.o
LIB_OBJS += util/exec_cmd.o
LIB_OBJS += util/help.o
LIB_OBJS += util/levenshtein.o
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 9e1638c..4a73d89 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -109,6 +109,12 @@ static void write_output(void *buf, size_t size)
}
}
+static int process_synthesized_event(event_t *event)
+{
+ write_output(event, event->header.size);
+ return 0;
+}
+
static void mmap_read(struct mmap_data *md)
{
unsigned int head = mmap_read_head(md);
@@ -191,172 +197,6 @@ static void sig_atexit(void)
kill(getpid(), signr);
}
-static pid_t pid_synthesize_comm_event(pid_t pid, int full)
-{
- struct comm_event comm_ev;
- char filename[PATH_MAX];
- char bf[BUFSIZ];
- FILE *fp;
- size_t size = 0;
- DIR *tasks;
- struct dirent dirent, *next;
- pid_t tgid = 0;
-
- snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
-
- fp = fopen(filename, "r");
- if (fp == NULL) {
-out_race:
- /*
- * We raced with a task exiting - just return:
- */
- if (verbose)
- fprintf(stderr, "couldn't open %s\n", filename);
- return 0;
- }
-
- memset(&comm_ev, 0, sizeof(comm_ev));
- while (!comm_ev.comm[0] || !comm_ev.pid) {
- if (fgets(bf, sizeof(bf), fp) == NULL)
- goto out_failure;
-
- if (memcmp(bf, "Name:", 5) == 0) {
- char *name = bf + 5;
- while (*name && isspace(*name))
- ++name;
- size = strlen(name) - 1;
- memcpy(comm_ev.comm, name, size++);
- } else if (memcmp(bf, "Tgid:", 5) == 0) {
- char *tgids = bf + 5;
- while (*tgids && isspace(*tgids))
- ++tgids;
- tgid = comm_ev.pid = atoi(tgids);
- }
- }
-
- comm_ev.header.type = PERF_RECORD_COMM;
- size = ALIGN(size, sizeof(u64));
- comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
-
- if (!full) {
- comm_ev.tid = pid;
-
- write_output(&comm_ev, comm_ev.header.size);
- goto out_fclose;
- }
-
- snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
-
- tasks = opendir(filename);
- if (tasks == NULL)
- goto out_race;
-
- while (!readdir_r(tasks, &dirent, &next) && next) {
- char *end;
- pid = strtol(dirent.d_name, &end, 10);
- if (*end)
- continue;
-
- comm_ev.tid = pid;
-
- write_output(&comm_ev, comm_ev.header.size);
- }
- closedir(tasks);
-
-out_fclose:
- fclose(fp);
- return tgid;
-
-out_failure:
- fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n",
- filename);
- exit(EXIT_FAILURE);
-}
-
-static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid)
-{
- char filename[PATH_MAX];
- FILE *fp;
-
- snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
-
- fp = fopen(filename, "r");
- if (fp == NULL) {
- /*
- * We raced with a task exiting - just return:
- */
- if (verbose)
- fprintf(stderr, "couldn't open %s\n", filename);
- return;
- }
- while (1) {
- char bf[BUFSIZ], *pbf = bf;
- struct mmap_event mmap_ev = {
- .header = { .type = PERF_RECORD_MMAP },
- };
- int n;
- size_t size;
- if (fgets(bf, sizeof(bf), fp) == NULL)
- break;
-
- /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
- n = hex2u64(pbf, &mmap_ev.start);
- if (n < 0)
- continue;
- pbf += n + 1;
- n = hex2u64(pbf, &mmap_ev.len);
- if (n < 0)
- continue;
- pbf += n + 3;
- if (*pbf == 'x') { /* vm_exec */
- char *execname = strchr(bf, '/');
-
- /* Catch VDSO */
- if (execname == NULL)
- execname = strstr(bf, "[vdso]");
-
- if (execname == NULL)
- continue;
-
- size = strlen(execname);
- execname[size - 1] = '\0'; /* Remove \n */
- memcpy(mmap_ev.filename, execname, size);
- size = ALIGN(size, sizeof(u64));
- mmap_ev.len -= mmap_ev.start;
- mmap_ev.header.size = (sizeof(mmap_ev) -
- (sizeof(mmap_ev.filename) - size));
- mmap_ev.pid = tgid;
- mmap_ev.tid = pid;
-
- write_output(&mmap_ev, mmap_ev.header.size);
- }
- }
-
- fclose(fp);
-}
-
-static void synthesize_all(void)
-{
- DIR *proc;
- struct dirent dirent, *next;
-
- proc = opendir("/proc");
-
- while (!readdir_r(proc, &dirent, &next) && next) {
- char *end;
- pid_t pid, tgid;
-
- pid = strtol(dirent.d_name, &end, 10);
- if (*end) /* only interested in proper numerical dirents */
- continue;
-
- tgid = pid_synthesize_comm_event(pid, 1);
- pid_synthesize_mmap_samples(pid, tgid);
- }
-
- closedir(proc);
-}
-
static int group_fd;
static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)
@@ -608,11 +448,10 @@ static int __cmd_record(int argc, const char **argv)
if (file_new)
perf_header__write(header, output);
- if (!system_wide) {
- pid_t tgid = pid_synthesize_comm_event(pid, 0);
- pid_synthesize_mmap_samples(pid, tgid);
- } else
- synthesize_all();
+ if (!system_wide)
+ event__synthesize_thread(pid, process_synthesized_event);
+ else
+ event__synthesize_threads(process_synthesized_event);
if (target_pid == -1 && argc) {
pid = fork();
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
new file mode 100644
index 0000000..1dae7e3
--- /dev/null
+++ b/tools/perf/util/event.c
@@ -0,0 +1,177 @@
+#include <linux/types.h>
+#include "event.h"
+#include "debug.h"
+#include "string.h"
+
+static pid_t event__synthesize_comm(pid_t pid, int full,
+ int (*process)(event_t *event))
+{
+ event_t ev;
+ char filename[PATH_MAX];
+ char bf[BUFSIZ];
+ FILE *fp;
+ size_t size = 0;
+ DIR *tasks;
+ struct dirent dirent, *next;
+ pid_t tgid = 0;
+
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+out_race:
+ /*
+ * We raced with a task exiting - just return:
+ */
+ pr_debug("couldn't open %s\n", filename);
+ return 0;
+ }
+
+ memset(&ev.comm, 0, sizeof(ev.comm));
+ while (!ev.comm.comm[0] || !ev.comm.pid) {
+ if (fgets(bf, sizeof(bf), fp) == NULL)
+ goto out_failure;
+
+ if (memcmp(bf, "Name:", 5) == 0) {
+ char *name = bf + 5;
+ while (*name && isspace(*name))
+ ++name;
+ size = strlen(name) - 1;
+ memcpy(ev.comm.comm, name, size++);
+ } else if (memcmp(bf, "Tgid:", 5) == 0) {
+ char *tgids = bf + 5;
+ while (*tgids && isspace(*tgids))
+ ++tgids;
+ tgid = ev.comm.pid = atoi(tgids);
+ }
+ }
+
+ ev.comm.header.type = PERF_RECORD_COMM;
+ size = ALIGN(size, sizeof(u64));
+ ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size);
+
+ if (!full) {
+ ev.comm.tid = pid;
+
+ process(&ev);
+ goto out_fclose;
+ }
+
+ snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
+
+ tasks = opendir(filename);
+ if (tasks == NULL)
+ goto out_race;
+
+ while (!readdir_r(tasks, &dirent, &next) && next) {
+ char *end;
+ pid = strtol(dirent.d_name, &end, 10);
+ if (*end)
+ continue;
+
+ ev.comm.tid = pid;
+
+ process(&ev);
+ }
+ closedir(tasks);
+
+out_fclose:
+ fclose(fp);
+ return tgid;
+
+out_failure:
+ pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
+ return -1;
+}
+
+static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
+ int (*process)(event_t *event))
+{
+ char filename[PATH_MAX];
+ FILE *fp;
+
+ snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ /*
+ * We raced with a task exiting - just return:
+ */
+ pr_debug("couldn't open %s\n", filename);
+ return -1;
+ }
+
+ while (1) {
+ char bf[BUFSIZ], *pbf = bf;
+ event_t ev = {
+ .header = { .type = PERF_RECORD_MMAP },
+ };
+ int n;
+ size_t size;
+ if (fgets(bf, sizeof(bf), fp) == NULL)
+ break;
+
+ /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
+ n = hex2u64(pbf, &ev.mmap.start);
+ if (n < 0)
+ continue;
+ pbf += n + 1;
+ n = hex2u64(pbf, &ev.mmap.len);
+ if (n < 0)
+ continue;
+ pbf += n + 3;
+ if (*pbf == 'x') { /* vm_exec */
+ char *execname = strchr(bf, '/');
+
+ /* Catch VDSO */
+ if (execname == NULL)
+ execname = strstr(bf, "[vdso]");
+
+ if (execname == NULL)
+ continue;
+
+ size = strlen(execname);
+ execname[size - 1] = '\0'; /* Remove \n */
+ memcpy(ev.mmap.filename, execname, size);
+ size = ALIGN(size, sizeof(u64));
+ ev.mmap.len -= ev.mmap.start;
+ ev.mmap.header.size = (sizeof(ev.mmap) -
+ (sizeof(ev.mmap.filename) - size));
+ ev.mmap.pid = tgid;
+ ev.mmap.tid = pid;
+
+ process(&ev);
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+int event__synthesize_thread(pid_t pid, int (*process)(event_t *event))
+{
+ pid_t tgid = event__synthesize_comm(pid, 1, process);
+ if (tgid == -1)
+ return -1;
+ return event__synthesize_mmap_events(pid, tgid, process);
+}
+
+void event__synthesize_threads(int (*process)(event_t *event))
+{
+ DIR *proc;
+ struct dirent dirent, *next;
+
+ proc = opendir("/proc");
+
+ while (!readdir_r(proc, &dirent, &next) && next) {
+ char *end;
+ pid_t pid = strtol(dirent.d_name, &end, 10);
+
+ if (*end) /* only interested in proper numerical dirents */
+ continue;
+
+ event__synthesize_thread(pid, process);
+ }
+
+ closedir(proc);
+}
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index d972b4b..2ae1177 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -111,4 +111,7 @@ struct map *map__clone(struct map *self);
int map__overlap(struct map *l, struct map *r);
size_t map__fprintf(struct map *self, FILE *fp);
+int event__synthesize_thread(pid_t pid, int (*process)(event_t *event));
+void event__synthesize_threads(int (*process)(event_t *event));
+
#endif /* __PERF_RECORD_H */
--
1.5.5.1
Compiling the kernel with 'make -k 22 allyesconfig'
[root@emilia linux-2.6-tip]# perf top -r 90
------------------------------------------------------------------------------
PerfTop: 3669 irqs/sec kernel:59.9% [1000Hz cycles], (all, 8 CPUs)
------------------------------------------------------------------------------
samples pcnt function DSO
_______ _____ ________________________________ ________________
3062.00 6.5% clear_page_c [kernel]
2233.00 4.8% _int_malloc /lib64/libc-2.5.so
2100.00 4.5% yylex /home/acme/git/build/allyesconfig/scripts/genksyms/genksyms
2029.00 4.3% memset /lib64/libc-2.5.so
1224.00 2.6% page_fault [kernel]
1075.00 2.3% __GI_strlen /lib64/libc-2.5.so
863.00 1.8% sub_preempt_count [kernel]
822.00 1.8% __GI_memcpy /lib64/libc-2.5.so
810.00 1.7% __GI_vfprintf /lib64/libc-2.5.so
786.00 1.7% _int_free /lib64/libc-2.5.so
775.00 1.7% __GI_strcmp /lib64/libc-2.5.so
748.00 1.6% _spin_lock [kernel]
699.00 1.5% main /home/acme/git/build/allyesconfig/scripts/basic/fixdep
659.00 1.4% add_preempt_count [kernel]
649.00 1.4% yyparse /home/acme/git/build/allyesconfig/scripts/genksyms/genksyms
645.00 1.4% preempt_trace [kernel]
635.00 1.4% __GI___libc_free /lib64/libc-2.5.so
597.00 1.3% trace_preempt_on [kernel]
551.00 1.2% __GI___libc_malloc /lib64/libc-2.5.so
516.00 1.1% _spin_lock_irqsave [kernel]
481.00 1.0% copy_user_generic_string [kernel]
479.00 1.0% unmap_vmas [kernel]
429.00 0.9% _IO_file_xsputn_internal /lib64/libc-2.5.so
425.00 0.9% __GI_strncpy /lib64/libc-2.5.so
416.00 0.9% get_page_from_freelist [kernel]
414.00 0.9% malloc_consolidate /lib64/libc-2.5.so
406.00 0.9% get_parent_ip [kernel]
362.00 0.8% __rmqueue [kernel]
347.00 0.7% in_lock_functions [kernel]
316.00 0.7% __d_lookup [kernel]
[root@emilia linux-2.6-tip]#
More polishing is needed to print just DSO basename when not --verbose, etc.
Supporting a 'comm' column requires some more reworking of 'perf top' internals
as we will need to use something like the hist entries 'perf report' uses and
will be done in another patch.
Cc: Frederic Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Mike Galbraith <efa...@gmx.de>
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
---
tools/perf/builtin-top.c | 143 ++++++++++++++++++++++++++++++++-------------
1 files changed, 101 insertions(+), 42 deletions(-)
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 4a9fe22..a02fc41 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -318,7 +318,7 @@ static void show_details(struct sym_entry *syme)
}
/*
- * Symbols will be added here in record_ip and will get out
+ * Symbols will be added here in event__process_sample and will get out
* after decayed.
*/
static LIST_HEAD(active_symbols);
@@ -459,18 +459,18 @@ static void print_sym_table(void)
}
if (nr_counters == 1)
- printf(" samples pcnt");
+ printf(" samples pcnt");
else
- printf(" weight samples pcnt");
+ printf(" weight samples pcnt");
if (verbose)
printf(" RIP ");
- printf(" kernel function\n");
- printf(" %s _______ _____",
+ printf(" function DSO\n");
+ printf(" %s _______ _____",
nr_counters == 1 ? " " : "______");
if (verbose)
- printf(" ________________");
- printf(" _______________\n\n");
+ printf(" ________________");
+ printf(" ________________________________ ________________\n\n");
for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
struct symbol *sym;
@@ -486,16 +486,15 @@ static void print_sym_table(void)
sum_ksamples));
if (nr_counters == 1 || !display_weighted)
- printf("%20.2f - ", syme->weight);
+ printf("%20.2f ", syme->weight);
else
- printf("%9.1f %10ld - ", syme->weight, syme->snap_count);
+ printf("%9.1f %10ld ", syme->weight, syme->snap_count);
percent_color_fprintf(stdout, "%4.1f%%", pcnt);
if (verbose)
- printf(" - %016llx", sym->start);
- printf(" : %s", sym->name);
- if (syme->map->dso->name[0] == '[')
- printf(" \t%s", syme->map->dso->name);
+ printf(" %016llx", sym->start);
+ printf(" %-32s", sym->name);
+ printf(" %s", syme->map->dso->short_name);
printf("\n");
}
}
@@ -818,41 +817,97 @@ static int parse_symbols(void)
return 0;
}
-/*
- * Binary search in the histogram table and record the hit:
- */
-static void record_ip(u64 ip, int counter)
+static void event__process_sample(const event_t *self, int counter)
{
+ u64 ip = self->ip.ip;
struct map *map;
- struct symbol *sym = kernel_maps__find_symbol(ip, &map);
-
- if (sym != NULL) {
- struct sym_entry *syme = dso__sym_priv(map->dso, sym);
-
- if (!syme->skip) {
- syme->count[counter]++;
- record_precise_ip(syme, counter, ip);
- pthread_mutex_lock(&active_symbols_lock);
- if (list_empty(&syme->node) || !syme->node.next)
- __list_insert_active_sym(syme);
- pthread_mutex_unlock(&active_symbols_lock);
+ struct sym_entry *syme;
+ struct symbol *sym;
+
+ switch (self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) {
+ case PERF_RECORD_MISC_USER: {
+ struct thread *thread = threads__findnew(self->ip.pid);
+
+ if (thread == NULL)
return;
+
+ map = thread__find_map(thread, ip);
+ if (map != NULL) {
+ ip = map->map_ip(map, ip);
+ sym = map->dso->find_symbol(map->dso, ip);
+ if (sym == NULL)
+ return;
+ userspace_samples++;
+ break;
}
}
+ /*
+ * If this is outside of all known maps,
+ * and is a negative address, try to look it
+ * up in the kernel dso, as it might be a
+ * vsyscall or vdso (which executes in user-mode).
+ */
+ if ((long long)ip >= 0)
+ return;
+ /* Fall thru */
+ case PERF_RECORD_MISC_KERNEL:
+ sym = kernel_maps__find_symbol(ip, &map);
+ if (sym == NULL)
+ return;
+ break;
+ default:
+ return;
+ }
+
+ syme = dso__sym_priv(map->dso, sym);
- samples--;
+ if (!syme->skip) {
+ syme->count[counter]++;
+ record_precise_ip(syme, counter, ip);
+ pthread_mutex_lock(&active_symbols_lock);
+ if (list_empty(&syme->node) || !syme->node.next)
+ __list_insert_active_sym(syme);
+ pthread_mutex_unlock(&active_symbols_lock);
+ ++samples;
+ return;
+ }
}
-static void process_event(u64 ip, int counter, int user)
+static void event__process_mmap(event_t *self)
{
- samples++;
+ struct thread *thread = threads__findnew(self->mmap.pid);
+
+ if (thread != NULL) {
+ struct map *map = map__new(&self->mmap, NULL, 0,
+ sizeof(struct sym_entry),
+ symbol_filter);
+ if (map != NULL)
+ thread__insert_map(thread, map);
+ }
+}
- if (user) {
- userspace_samples++;
- return;
+static void event__process_comm(event_t *self)
+{
+ struct thread *thread = threads__findnew(self->comm.pid);
+
+ if (thread != NULL)
+ thread__set_comm(thread, self->comm.comm);
+}
+
+static int event__process(event_t *event)
+{
+ switch (event->header.type) {
+ case PERF_RECORD_COMM:
+ event__process_comm(event);
+ break;
+ case PERF_RECORD_MMAP:
+ event__process_mmap(event);
+ break;
+ default:
+ break;
}
- record_ip(ip, counter);
+ return 0;
}
struct mmap_data {
@@ -925,13 +980,11 @@ static void mmap_read_counter(struct mmap_data *md)
event = &event_copy;
}
+ if (event->header.type == PERF_RECORD_SAMPLE)
+ event__process_sample(event, md->counter);
+ else
+ event__process(event);
old += size;
-
- if (event->header.type == PERF_RECORD_SAMPLE) {
- int user =
- (event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER;
- process_event(event->ip.ip, md->counter, user);
- }
}
md->prev = old;
@@ -973,6 +1026,7 @@ static void start_counter(int i, int counter)
}
attr->inherit = (cpu < 0) && inherit;
+ attr->mmap = 1;
try_again:
fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0);
@@ -1031,6 +1085,11 @@ static int __cmd_top(void)
int i, counter;
int ret;
+ if (target_pid != -1)
+ event__synthesize_thread(target_pid, event__process);
+ else
+ event__synthesize_threads(event__process);
+
for (i = 0; i < nr_cpus; i++) {
group_fd = -1;
for (counter = 0; counter < nr_counters; counter++)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index ac5ddff..9e1638c 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -206,6 +206,7 @@ static pid_t pid_synthesize_comm_event(pid_t pid, int full)
fp = fopen(filename, "r");
if (fp == NULL) {
+out_race:
/*
* We raced with a task exiting - just return:
*/
@@ -247,6 +248,9 @@ static pid_t pid_synthesize_comm_event(pid_t pid, int full)
snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
tasks = opendir(filename);
+ if (tasks == NULL)
+ goto out_race;
+
while (!readdir_r(tasks, &dirent, &next) && next) {
char *end;
pid = strtol(dirent.d_name, &end, 10);
perf record: Fix race where process can disappear while reading its /proc/pid/tasks
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
Cc: Frederic Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Mike Galbraith <efa...@gmx.de>
LKML-Reference: <1256592199-9608-1-...@redhat.com>
Signed-off-by: Ingo Molnar <mi...@elte.hu>
---
tools/perf/builtin-record.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index ac5ddff..9e1638c 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -206,6 +206,7 @@ static pid_t pid_synthesize_comm_event(pid_t pid, int full)
fp = fopen(filename, "r");
if (fp == NULL) {
+out_race:
/*
* We raced with a task exiting - just return:
*/
@@ -247,6 +248,9 @@ static pid_t pid_synthesize_comm_event(pid_t pid, int full)
snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
tasks = opendir(filename);
+ if (tasks == NULL)
+ goto out_race;
+
while (!readdir_r(tasks, &dirent, &next) && next) {
char *end;
pid = strtol(dirent.d_name, &end, 10);
--
perf tools: Generalize event synthesizing routines
Because we will need it in 'perf top' to support userspace
symbols for existing threads.
Now we pass a callback that will receive the synthesized event
and then write it to the output file in 'perf record' and in the
upcoming patch for 'perf top' we will just immediatelly create
the in memory representation of threads and maps.
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
Cc: Frederic Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Mike Galbraith <efa...@gmx.de>
LKML-Reference: <1256592199-9608-2-...@redhat.com>
Signed-off-by: Ingo Molnar <mi...@elte.hu>
---
tools/perf/Makefile | 1 +
tools/perf/builtin-record.c | 181 +++----------------------------------------
tools/perf/util/event.c | 177 ++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/event.h | 3 +
4 files changed, 191 insertions(+), 171 deletions(-)
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 0a40c29..9f4488d 100644
That's a really nice feature! I tried it and works well here too.
Ingo
perf top: Support userspace symbols too
Example:
[root@emilia linux-2.6-tip]#
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
Cc: Frederic Weisbecker <fwei...@gmail.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Mike Galbraith <efa...@gmx.de>
LKML-Reference: <1256592199-9608-3-...@redhat.com>
Signed-off-by: Ingo Molnar <mi...@elte.hu>
Signed-off-by: Thiago Farina <tfra...@gmail.com>
---
drivers/net/tehuti.c | 4 ++--
drivers/net/tehuti.h | 2 --
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c
index ec9dfb2..8d116a9 100644
--- a/drivers/net/tehuti.c
+++ b/drivers/net/tehuti.c
@@ -1878,7 +1878,7 @@ static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size)
udelay(50); /* give hw a chance to clean fifo */
continue;
}
- avail = MIN(avail, size);
+ avail = min(avail, size);
DBG("about to push %d bytes starting %p size %d\n", avail,
data, size);
bdx_tx_push_desc(priv, data, avail);
@@ -1889,7 +1889,7 @@ static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size)
}
static const struct net_device_ops bdx_netdev_ops = {
- .ndo_open = bdx_open,
+ .ndo_open = bdx_open,
.ndo_stop = bdx_close,
.ndo_start_xmit = bdx_tx_transmit,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/tehuti.h b/drivers/net/tehuti.h
index 4fc875e..1241419 100644
--- a/drivers/net/tehuti.h
+++ b/drivers/net/tehuti.h
@@ -76,8 +76,6 @@
#define FIFO_SIZE 4096
#define FIFO_EXTRA_SPACE 1024
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-
#if BITS_PER_LONG == 64
# define H32_64(x) (u32) ((u64)(x) >> 32)
# define L32_64(x) (u32) ((u64)(x) & 0xffffffff)
--
1.6.5.1.61.ge79999
Why are you change TAB to SPACES here?
> .ndo_stop = bdx_close,
> .ndo_start_xmit = bdx_tx_transmit,
> .ndo_validate_addr = eth_validate_addr,
> diff --git a/drivers/net/tehuti.h b/drivers/net/tehuti.h
> index 4fc875e..1241419 100644
> --- a/drivers/net/tehuti.h
> +++ b/drivers/net/tehuti.h
> @@ -76,8 +76,6 @@
> #define FIFO_SIZE 4096
> #define FIFO_EXTRA_SPACE 1024
>
> -#define MIN(x, y) ((x) < (y) ? (x) : (y))
> -
> #if BITS_PER_LONG == 64
> # define H32_64(x) (u32) ((u64)(x) >> 32)
> # define L32_64(x) (u32) ((u64)(x) & 0xffffffff)
--
Regards
Yang Hongyang
Signed-off-by: Thiago Farina <tfra...@gmail.com>
---
drivers/net/tehuti.c | 2 +-
drivers/net/tehuti.h | 2 --
2 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c
index ec9dfb2..863084d 100644
--- a/drivers/net/tehuti.c
+++ b/drivers/net/tehuti.c
@@ -1878,7 +1878,7 @@ static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size)
udelay(50); /* give hw a chance to clean fifo */
continue;
}
- avail = MIN(avail, size);
+ avail = min(avail, size);
DBG("about to push %d bytes starting %p size %d\n", avail,
data, size);
bdx_tx_push_desc(priv, data, avail);
diff --git a/drivers/net/tehuti.h b/drivers/net/tehuti.h
index 4fc875e..1241419 100644
--- a/drivers/net/tehuti.h
+++ b/drivers/net/tehuti.h
@@ -76,8 +76,6 @@
#define FIFO_SIZE 4096
#define FIFO_EXTRA_SPACE 1024
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-
#if BITS_PER_LONG == 64
# define H32_64(x) (u32) ((u64)(x) >> 32)
# define L32_64(x) (u32) ((u64)(x) & 0xffffffff)
--
1.6.5.1.61.ge79999
> Since the kernel api already has the macro "min",
> just use it instead of declaring another one.
>
> Signed-off-by: Thiago Farina <tfra...@gmail.com>
Applied to net-next-2.6, thanks!
I did ftrace support for Microblaze. I would like to ask you for review.
I don't expect too much troubles. The main thing is part of recordmcount.pl
- here could be a small problem because of MIPS patches.
It is based on latest Linus tree.
I have that patches in my tree and they are in next tree too. They will go through my tree.
Thanks,
Michal
This patch add support for dynamic function graph tracer.
There is one my expactation that I can do flush_icache after
all code modification. On microblaze is this safer than do
flush for every entry. For icache is used name flush but
correct should be invalidation - this will be fix in upcomming
new cache implementaion and WB support.
Signed-off-by: Michal Simek <mon...@monstr.eu>
---
arch/microblaze/kernel/ftrace.c | 28 ++++++++++++++++++++++++++++
arch/microblaze/kernel/mcount.S | 6 ++++++
2 files changed, 34 insertions(+), 0 deletions(-)
diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c
index 0952a8b..388b31c 100644
--- a/arch/microblaze/kernel/ftrace.c
+++ b/arch/microblaze/kernel/ftrace.c
@@ -206,4 +206,32 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return ret;
}
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+unsigned int old_jump; /* saving place for jump instruction */
+
+int ftrace_enable_ftrace_graph_caller(void)
+{
+ unsigned int ret;
+ unsigned long ip = (unsigned long)(&ftrace_call_graph);
+
+ old_jump = *(unsigned int *)ip; /* save jump over instruction */
+ ret = ftrace_modify_code(ip, MICROBLAZE_NOP);
+ flush_icache();
+
+ pr_debug("%s: Replace instruction: 0x%x\n", __func__, old_jump);
+ return ret;
+}
+
+int ftrace_disable_ftrace_graph_caller(void)
+{
+ unsigned int ret;
+ unsigned long ip = (unsigned long)(&ftrace_call_graph);
+
+ ret = ftrace_modify_code(ip, old_jump);
+ flush_icache();
+
+ pr_debug("%s\n", __func__);
+ return ret;
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/microblaze/kernel/mcount.S b/arch/microblaze/kernel/mcount.S
index 84a1945..e7eaa7a 100644
--- a/arch/microblaze/kernel/mcount.S
+++ b/arch/microblaze/kernel/mcount.S
@@ -97,6 +97,7 @@ ENTRY(ftrace_caller)
nop;
/* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST end of checking */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+#ifndef CONFIG_DYNAMIC_FTRACE
lwi r5, r0, ftrace_graph_return;
addik r6, r0, ftrace_stub; /* asm implementation */
cmpu r5, r5, r6; /* ftrace_graph_return != ftrace_stub */
@@ -108,6 +109,11 @@ ENTRY(ftrace_caller)
cmpu r5, r5, r6; /* ftrace_graph_entry != ftrace_graph_entry_stub */
beqid r5, end_graph_tracer;
nop;
+#else /* CONFIG_DYNAMIC_FTRACE */
+NOALIGN_ENTRY(ftrace_call_graph)
+ /* MS: jump over graph function - replaced from C code */
+ bri end_graph_tracer
+#endif /* CONFIG_DYNAMIC_FTRACE */
addik r5, r1, 120; /* MS: load parent addr */
addik r6, r15, 0; /* MS: load current function addr */
bralid r15, prepare_ftrace_return;
--
1.5.5.1
With dynamic function tracer, by default, _mcount is defined as an
"empty" function, it returns directly without any more action. When
enabling it in user-space, it will jump to a real tracing
function(ftrace_caller), and do the real job for us.
Differ from the static function tracer, dynamic function tracer provides
two functions ftrace_make_call()/ftrace_make_nop() to enable/disable the
tracing of some indicated kernel functions(set_ftrace_filter).
In the kernel version, there is only one "_mcount" string for every
kernel function, so, we just need to match this one in mcount_regex of
scripts/recordmcount.pl.
For more information please look at code and Documentation/trace folder.
Signed-off-by: Michal Simek <mon...@monstr.eu>
---
arch/microblaze/Kconfig | 2 +
arch/microblaze/include/asm/ftrace.h | 11 +++
arch/microblaze/kernel/Makefile | 2 +-
arch/microblaze/kernel/ftrace.c | 151 ++++++++++++++++++++++++++++++++++
arch/microblaze/kernel/mcount.S | 13 +++
scripts/recordmcount.pl | 3 +
6 files changed, 181 insertions(+), 1 deletions(-)
create mode 100644 arch/microblaze/kernel/ftrace.c
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig
index cccf3ad..102d73a 100644
--- a/arch/microblaze/Kconfig
+++ b/arch/microblaze/Kconfig
@@ -8,6 +8,8 @@ config MICROBLAZE
select HAVE_LMB
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_TRACE_MCOUNT_TEST
+ select HAVE_DYNAMIC_FTRACE
+ select HAVE_FTRACE_MCOUNT_RECORD
select USB_ARCH_HAS_EHCI
select ARCH_WANT_OPTIONAL_GPIOLIB
diff --git a/arch/microblaze/include/asm/ftrace.h b/arch/microblaze/include/asm/ftrace.h
index 22beec5..fd2fa2e 100644
--- a/arch/microblaze/include/asm/ftrace.h
+++ b/arch/microblaze/include/asm/ftrace.h
@@ -11,5 +11,16 @@ extern void _mcount(void);
extern void ftrace_call_graph(void);
#endif
+#ifdef CONFIG_DYNAMIC_FTRACE
+/* reloction of mcount call site is the same as the address */
+static inline unsigned long ftrace_call_adjust(unsigned long addr)
+{
+ return addr;
+}
+
+struct dyn_arch_ftrace {
+};
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
#endif /* CONFIG_FUNCTION_TRACER */
#endif /* _ASM_MICROBLAZE_FTRACE */
diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile
index d5ee326..b07594e 100644
--- a/arch/microblaze/kernel/Makefile
+++ b/arch/microblaze/kernel/Makefile
@@ -27,6 +27,6 @@ obj-$(CONFIG_HEART_BEAT) += heartbeat.o
obj-$(CONFIG_MODULES) += microblaze_ksyms.o module.o
obj-$(CONFIG_MMU) += misc.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
-obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
+obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o
obj-y += entry$(MMU).o
diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c
new file mode 100644
index 0000000..c1889b1
--- /dev/null
+++ b/arch/microblaze/kernel/ftrace.c
@@ -0,0 +1,151 @@
+/*
+ * Ftrace support for Microblaze.
+ *
+ * Copyright (C) 2009 Michal Simek <mon...@monstr.eu>
+ * Copyright (C) 2009 PetaLogix
+ *
+ * Based on MIPS and PowerPC ftrace code
+ *
+ * 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.
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/ftrace.h>
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+/* save value to addr - it is save to do it in asm */
+static int ftrace_modify_code(unsigned long addr, unsigned int value)
+{
+ int faulted = 0;
+
+ __asm__ __volatile__(" 1: swi %2, %1, 0; \
+ addik %0, r0, 0; \
+ 2: \
+ .section .fixup, \"ax\"; \
+ 3: brid 2b; \
+ addik %0, r0, 1; \
+ .previous; \
+ .section __ex_table,\"a\"; \
+ .word 1b,3b; \
+ .previous;" \
+ : "=r" (faulted)
+ : "r" (addr), "r" (value)
+ );
+
+ if (unlikely(faulted))
+ return -EFAULT;
+
+ return 0;
+}
+
+#define MICROBLAZE_NOP 0x80000000
+#define MICROBLAZE_BRI 0xb800000C
+
+static unsigned int recorded; /* if save was or not */
+static unsigned int imm; /* saving whole imm instruction */
+
+/* There are two approaches howto solve ftrace_make nop function - look below */
+#undef USE_FTRACE_NOP
+
+#ifdef USE_FTRACE_NOP
+static unsigned int bralid; /* saving whole bralid instruction */
+#endif
+
+int ftrace_make_nop(struct module *mod,
+ struct dyn_ftrace *rec, unsigned long addr)
+{
+ /* we have this part of code which we are working with
+ * b000c000 imm -16384
+ * b9fc8e30 bralid r15, -29136 // c0008e30 <_mcount>
+ * 80000000 or r0, r0, r0
+ *
+ * The first solution (!USE_FTRACE_NOP-could be called branch solution)
+ * b000c000 bri 12 (0xC - jump to any other instruction)
+ * b9fc8e30 bralid r15, -29136 // c0008e30 <_mcount>
+ * 80000000 or r0, r0, r0
+ * any other instruction
+ *
+ * The second solution (USE_FTRACE_NOP) - no jump just nops
+ * 80000000 or r0, r0, r0
+ * 80000000 or r0, r0, r0
+ * 80000000 or r0, r0, r0
+ */
+ int ret = 0;
+
+ if (recorded == 0) {
+ recorded = 1;
+ imm = *(unsigned int *)rec->ip;
+ pr_debug("%s: imm:0x%x\n", __func__, imm);
+#ifdef USE_FTRACE_NOP
+ bralid = *(unsigned int *)(rec->ip + 4);
+ pr_debug("%s: bralid 0x%x\n", __func__, bralid);
+#endif /* USE_FTRACE_NOP */
+ }
+
+#ifdef USE_FTRACE_NOP
+ ret = ftrace_modify_code(rec->ip, MICROBLAZE_NOP);
+ ret += ftrace_modify_code(rec->ip + 4, MICROBLAZE_NOP);
+#else /* USE_FTRACE_NOP */
+ ret = ftrace_modify_code(rec->ip, MICROBLAZE_BRI);
+#endif /* USE_FTRACE_NOP */
+ return ret;
+}
+
+static int ret_addr; /* initialized as 0 by default */
+
+/* I believe that first is called ftrace_make_nop before this function */
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+ int ret;
+ ret_addr = addr; /* saving where the barrier jump is */
+ pr_debug("%s: addr:0x%x, rec->ip: 0x%x, imm:0x%x\n",
+ __func__, (unsigned int)addr, (unsigned int)rec->ip, imm);
+ ret = ftrace_modify_code(rec->ip, imm);
+#ifdef USE_FTRACE_NOP
+ pr_debug("%s: bralid:0x%x\n", __func__, bralid);
+ ret += ftrace_modify_code(rec->ip + 4, bralid);
+#endif /* USE_FTRACE_NOP */
+ return ret;
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+ /* The return code is retured via data */
+ *(unsigned long *)data = 0;
+
+ return 0;
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ unsigned long ip = (unsigned long)(&ftrace_call);
+ unsigned int upper = (unsigned int)func;
+ unsigned int lower = (unsigned int)func;
+ int ret = 0;
+
+ /* create proper saving to ftrace_call poll */
+ upper = 0xb0000000 + (upper >> 16); /* imm func_upper */
+ lower = 0x32800000 + (lower & 0xFFFF); /* addik r20, r0, func_lower */
+
+ pr_debug("%s: func=0x%x, ip=0x%x, upper=0x%x, lower=0x%x\n",
+ __func__, (unsigned int)func, (unsigned int)ip, upper, lower);
+
+ /* save upper and lower code */
+ ret = ftrace_modify_code(ip, upper);
+ ret += ftrace_modify_code(ip + 4, lower);
+
+ /* We just need to remove the rtsd r15, 8 by NOP */
+ BUG_ON(!ret_addr);
+ if (ret_addr)
+ ret += ftrace_modify_code(ret_addr, MICROBLAZE_NOP);
+ else
+ ret = 1; /* fault */
+
+ /* All changes are done - lets do caches consistent */
+ flush_icache();
+ return ret;
+}
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/microblaze/kernel/mcount.S b/arch/microblaze/kernel/mcount.S
index 97eef3e..30aaf8f 100644
--- a/arch/microblaze/kernel/mcount.S
+++ b/arch/microblaze/kernel/mcount.S
@@ -83,6 +83,12 @@ ENTRY(ftrace_stub)
nop;
ENTRY(_mcount)
+#ifdef CONFIG_DYNAMIC_FTRACE
+ENTRY(ftrace_caller)
+ /* MS: It is just barrier which is removed from C code */
+ rtsd r15, 8
+ nop
+#endif /* CONFIG_DYNAMIC_FTRACE */
SAVE_REGS
swi r15, r1, 0;
/* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST begin of checking */
@@ -90,12 +96,19 @@ ENTRY(_mcount)
bneid r5, end;
nop;
/* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST end of checking */
+#ifndef CONFIG_DYNAMIC_FTRACE
/* MS: test function trace if is taken or not */
lwi r20, r0, ftrace_trace_function;
addik r6, r0, ftrace_stub;
cmpu r5, r20, r6; /* ftrace_trace_function != ftrace_stub */
beqid r5, end; /* MS: not taken -> jump over */
nop;
+#else /* CONFIG_DYNAMIC_FTRACE */
+NOALIGN_ENTRY(ftrace_call)
+/* instruction for setup imm FUNC_part1, addik r20, r0, FUNC_part2 */
+ nop
+ nop
+#endif /* CONFIG_DYNAMIC_FTRACE */
/* static normal trace */
lwi r6, r1, 120; /* MS: load parent addr */
addik r5, r15, 0; /* MS: load current function addr */
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index 090d300..b30dabd 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -245,6 +245,9 @@ if ($arch eq "x86_64") {
$ld .= " -m elf64_sparc";
$cc .= " -m64";
$objcopy .= " -O elf64-sparc";
+} elsif ($arch eq "microblaze") {
+ # Microblaze calls '_mcount' instead of plain 'mcount'.
+ $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$";
} else {
die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
}
--
1.5.5.1
For more information look at Documentation/trace folder.
Signed-off-by: Michal Simek <mon...@monstr.eu>
---
arch/microblaze/Kconfig | 1 +
arch/microblaze/kernel/ftrace.c | 58 +++++++++++++++++++++++++++++++++++++++
arch/microblaze/kernel/mcount.S | 41 +++++++++++++++++++++++++++
3 files changed, 100 insertions(+), 0 deletions(-)
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig
index 102d73a..e297802 100644
--- a/arch/microblaze/Kconfig
+++ b/arch/microblaze/Kconfig
@@ -8,6 +8,7 @@ config MICROBLAZE
select HAVE_LMB
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_TRACE_MCOUNT_TEST
+ select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_DYNAMIC_FTRACE
select HAVE_FTRACE_MCOUNT_RECORD
select USB_ARCH_HAS_EHCI
diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c
index c1889b1..0952a8b 100644
--- a/arch/microblaze/kernel/ftrace.c
+++ b/arch/microblaze/kernel/ftrace.c
@@ -14,6 +14,64 @@
#include <asm/cacheflush.h>
#include <linux/ftrace.h>
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+/*
+ * Hook the return address and push it in the stack of return addrs
+ * in current thread info.
+ */
+void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
+{
+ unsigned long old;
+ int faulted, err;
+ struct ftrace_graph_ent trace;
+ unsigned long return_hooker = (unsigned long)
+ &return_to_handler;
+
+ if (unlikely(atomic_read(¤t->tracing_graph_pause)))
+ return;
+
+ /*
+ * Protect against fault, even if it shouldn't
+ * happen. This tool is too much intrusive to
+ * ignore such a protection.
+ */
+ asm volatile(" 1: lwi %0, %2, 0; \
+ 2: swi %3, %2, 0; \
+ addik %1, r0, 0; \
+ 3: \
+ .section .fixup, \"ax\"; \
+ 4: brid 3b; \
+ addik %1, r0, 1; \
+ .previous; \
+ .section __ex_table,\"a\"; \
+ .word 1b,4b; \
+ .word 2b,4b; \
+ .previous;" \
+ : "=&r" (old), "=r" (faulted)
+ : "r" (parent), "r" (return_hooker)
+ );
+
+ if (unlikely(faulted)) {
+ ftrace_graph_stop();
+ WARN_ON(1);
+ return;
+ }
+
+ err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0);
+ if (err == -EBUSY) {
+ *parent = old;
+ return;
+ }
+
+ trace.func = self_addr;
+ /* Only trace if the calling function expects to */
+ if (!ftrace_graph_entry(&trace)) {
+ current->curr_ret_stack--;
+ *parent = old;
+ }
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
#ifdef CONFIG_DYNAMIC_FTRACE
/* save value to addr - it is save to do it in asm */
static int ftrace_modify_code(unsigned long addr, unsigned int value)
diff --git a/arch/microblaze/kernel/mcount.S b/arch/microblaze/kernel/mcount.S
index 30aaf8f..84a1945 100644
--- a/arch/microblaze/kernel/mcount.S
+++ b/arch/microblaze/kernel/mcount.S
@@ -96,6 +96,27 @@ ENTRY(ftrace_caller)
bneid r5, end;
nop;
/* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST end of checking */
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ lwi r5, r0, ftrace_graph_return;
+ addik r6, r0, ftrace_stub; /* asm implementation */
+ cmpu r5, r5, r6; /* ftrace_graph_return != ftrace_stub */
+ beqid r5, end_graph_tracer;
+ nop;
+
+ lwi r6, r0, ftrace_graph_entry;
+ addik r5, r0, ftrace_graph_entry_stub; /* implemented in C */
+ cmpu r5, r5, r6; /* ftrace_graph_entry != ftrace_graph_entry_stub */
+ beqid r5, end_graph_tracer;
+ nop;
+ addik r5, r1, 120; /* MS: load parent addr */
+ addik r6, r15, 0; /* MS: load current function addr */
+ bralid r15, prepare_ftrace_return;
+ nop;
+ /* MS: graph was taken that's why - can jump over function trace */
+ brid end;
+ nop;
+end_graph_tracer:
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
#ifndef CONFIG_DYNAMIC_FTRACE
/* MS: test function trace if is taken or not */
lwi r20, r0, ftrace_trace_function;
@@ -121,3 +142,23 @@ end:
rtsd r15, 8; /* MS: jump back */
nop;
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ENTRY(return_to_handler)
+ nop; /* MS: just barrier for rtsd r15, 8 */
+ nop;
+ SAVE_REGS
+ swi r15, r1, 0;
+
+ /* MS: find out returning address */
+ bralid r15, ftrace_return_to_handler;
+ nop;
+
+ /* MS: return value from ftrace_return_to_handler is my returning addr
+ * must be before restore regs because I have to restore r3 content */
+ addik r15, r3, 0;
+ RESTORE_REGS
+
+ rtsd r15, 8; /* MS: jump back */
+ nop;
+#endif /* CONFIG_FUNCTION_TRACER */
If -pg of gcc is enabled with CONFIG_FUNCTION_TRACER=y. a calling to
_mcount will be inserted into each kernel function. so, there is a
possibility to trace the kernel functions in _mcount.
This patch add the specific _mcount support for static function
tracing. by default, ftrace_trace_function is initialized as
ftrace_stub(an empty function), so, the default _mcount will introduce
very little overhead. after enabling ftrace in user-space, it will jump
to a real tracing function and do static function tracing for us.
Commit message from Wu Zhangjin <wuzha...@gmail.com>
Signed-off-by: Michal Simek <mon...@monstr.eu>
---
arch/microblaze/Kconfig | 1 +
arch/microblaze/include/asm/ftrace.h | 14 ++++
arch/microblaze/kernel/Makefile | 11 +++
arch/microblaze/kernel/cpu/Makefile | 4 +
arch/microblaze/kernel/mcount.S | 105 +++++++++++++++++++++++++++++
arch/microblaze/kernel/microblaze_ksyms.c | 5 ++
6 files changed, 140 insertions(+), 0 deletions(-)
create mode 100644 arch/microblaze/kernel/mcount.S
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig
index 57eeaa6..18003ca 100644
--- a/arch/microblaze/Kconfig
+++ b/arch/microblaze/Kconfig
@@ -6,6 +6,7 @@ mainmenu "Linux/Microblaze Kernel Configuration"
config MICROBLAZE
def_bool y
select HAVE_LMB
+ select HAVE_FUNCTION_TRACER
select USB_ARCH_HAS_EHCI
select ARCH_WANT_OPTIONAL_GPIOLIB
diff --git a/arch/microblaze/include/asm/ftrace.h b/arch/microblaze/include/asm/ftrace.h
index 8b13789..22beec5 100644
--- a/arch/microblaze/include/asm/ftrace.h
+++ b/arch/microblaze/include/asm/ftrace.h
@@ -1 +1,15 @@
+#ifndef _ASM_MICROBLAZE_FTRACE
+#define _ASM_MICROBLAZE_FTRACE
+#ifdef CONFIG_FUNCTION_TRACER
+
+#define MCOUNT_ADDR ((long)(_mcount))
+#define MCOUNT_INSN_SIZE 8 /* sizeof mcount call */
+
+#ifndef __ASSEMBLY__
+extern void _mcount(void);
+extern void ftrace_call_graph(void);
+#endif
+
+#endif /* CONFIG_FUNCTION_TRACER */
+#endif /* _ASM_MICROBLAZE_FTRACE */
diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile
index c465a5c..d5ee326 100644
--- a/arch/microblaze/kernel/Makefile
+++ b/arch/microblaze/kernel/Makefile
@@ -2,6 +2,16 @@
# Makefile
#
+ifdef CONFIG_FUNCTION_TRACER
+# Do not trace early boot code and low level code
+CFLAGS_REMOVE_timer.o = -pg
+CFLAGS_REMOVE_intc.o = -pg
+CFLAGS_REMOVE_early_printk.o = -pg
+CFLAGS_REMOVE_selfmod.o = -pg
+CFLAGS_REMOVE_heartbeat.o = -pg
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
extra-y := head.o vmlinux.lds
obj-y += exceptions.o \
@@ -17,5 +27,6 @@ obj-$(CONFIG_HEART_BEAT) += heartbeat.o
obj-$(CONFIG_MODULES) += microblaze_ksyms.o module.o
obj-$(CONFIG_MMU) += misc.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
+obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
obj-y += entry$(MMU).o
diff --git a/arch/microblaze/kernel/cpu/Makefile b/arch/microblaze/kernel/cpu/Makefile
index 20646e5..59cc7bc 100644
--- a/arch/microblaze/kernel/cpu/Makefile
+++ b/arch/microblaze/kernel/cpu/Makefile
@@ -2,6 +2,10 @@
# Build the appropriate CPU version support
#
+ifdef CONFIG_FUNCTION_TRACER
+CFLAGS_REMOVE_cache.o = -pg
+endif
+
EXTRA_CFLAGS += -DCPU_MAJOR=$(CPU_MAJOR) -DCPU_MINOR=$(CPU_MINOR) \
-DCPU_REV=$(CPU_REV)
diff --git a/arch/microblaze/kernel/mcount.S b/arch/microblaze/kernel/mcount.S
new file mode 100644
index 0000000..a257a1b
--- /dev/null
+++ b/arch/microblaze/kernel/mcount.S
@@ -0,0 +1,105 @@
+/*
+ * Low-level ftrace handling
+ *
+ * Copyright (C) 2009 Michal Simek <mon...@monstr.eu>
+ * Copyright (C) 2009 PetaLogix
+ *
+ * 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.
+ */
+
+#include <linux/linkage.h>
+
+#define NOALIGN_ENTRY(name) .globl name; name:
+
+/* FIXME MS: I think that I don't need to save all regs */
+#define SAVE_REGS \
+ addik r1, r1, -120; \
+ swi r2, r1, 4; \
+ swi r3, r1, 8; \
+ swi r4, r1, 12; \
+ swi r5, r1, 116; \
+ swi r6, r1, 16; \
+ swi r7, r1, 20; \
+ swi r8, r1, 24; \
+ swi r9, r1, 28; \
+ swi r10, r1, 32; \
+ swi r11, r1, 36; \
+ swi r12, r1, 40; \
+ swi r13, r1, 44; \
+ swi r14, r1, 48; \
+ swi r16, r1, 52; \
+ swi r17, r1, 56; \
+ swi r18, r1, 60; \
+ swi r19, r1, 64; \
+ swi r20, r1, 68; \
+ swi r21, r1, 72; \
+ swi r22, r1, 76; \
+ swi r23, r1, 80; \
+ swi r24, r1, 84; \
+ swi r25, r1, 88; \
+ swi r26, r1, 92; \
+ swi r27, r1, 96; \
+ swi r28, r1, 100; \
+ swi r29, r1, 104; \
+ swi r30, r1, 108; \
+ swi r31, r1, 112;
+
+#define RESTORE_REGS \
+ lwi r2, r1, 4; \
+ lwi r3, r1, 8; \
+ lwi r4, r1, 12; \
+ lwi r5, r1, 116; \
+ lwi r6, r1, 16; \
+ lwi r7, r1, 20; \
+ lwi r8, r1, 24; \
+ lwi r9, r1, 28; \
+ lwi r10, r1, 32; \
+ lwi r11, r1, 36; \
+ lwi r12, r1, 40; \
+ lwi r13, r1, 44; \
+ lwi r14, r1, 48; \
+ lwi r16, r1, 52; \
+ lwi r17, r1, 56; \
+ lwi r18, r1, 60; \
+ lwi r19, r1, 64; \
+ lwi r20, r1, 68; \
+ lwi r21, r1, 72; \
+ lwi r22, r1, 76; \
+ lwi r23, r1, 80; \
+ lwi r24, r1, 84; \
+ lwi r25, r1, 88; \
+ lwi r26, r1, 92; \
+ lwi r27, r1, 96; \
+ lwi r28, r1, 100; \
+ lwi r29, r1, 104; \
+ lwi r30, r1, 108; \
+ lwi r31, r1, 112; \
+ addik r1, r1, 120;
+
+ENTRY(ftrace_stub)
+ rtsd r15, 8;
+ nop;
+
+ENTRY(_mcount)
+ SAVE_REGS
+ swi r15, r1, 0;
+ /* MS: test function trace if is taken or not */
+ lwi r20, r0, ftrace_trace_function;
+ addik r6, r0, ftrace_stub;
+ cmpu r5, r20, r6; /* ftrace_trace_function != ftrace_stub */
+ beqid r5, end; /* MS: not taken -> jump over */
+ nop;
+/* static normal trace */
+ lwi r6, r1, 120; /* MS: load parent addr */
+ addik r5, r15, 0; /* MS: load current function addr */
+ /* MS: here is dependency on previous code */
+ brald r15, r20; /* MS: jump to ftrace handler */
+ nop;
+end:
+ lwi r15, r1, 0;
+ RESTORE_REGS
+
+ rtsd r15, 8; /* MS: jump back */
+ nop;
diff --git a/arch/microblaze/kernel/microblaze_ksyms.c b/arch/microblaze/kernel/microblaze_ksyms.c
index 59ff20e..bc4dcb7 100644
--- a/arch/microblaze/kernel/microblaze_ksyms.c
+++ b/arch/microblaze/kernel/microblaze_ksyms.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <asm/page.h>
#include <asm/system.h>
+#include <linux/ftrace.h>
#include <linux/uaccess.h>
/*
@@ -47,3 +48,7 @@ extern void __umodsi3(void);
EXPORT_SYMBOL(__umodsi3);
extern char *_ebss;
EXPORT_SYMBOL_GPL(_ebss);
+#ifdef CONFIG_FUNCTION_TRACER
+extern void _mcount(void);
+EXPORT_SYMBOL(_mcount);
+#endif
--
1.5.5.1
Implement MCOUNT_TEST in asm code - it is faster than use
generic code
Signed-off-by: Michal Simek <mon...@monstr.eu>
---
arch/microblaze/Kconfig | 1 +
arch/microblaze/kernel/mcount.S | 5 +++++
2 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig
index 18003ca..cccf3ad 100644
--- a/arch/microblaze/Kconfig
+++ b/arch/microblaze/Kconfig
@@ -7,6 +7,7 @@ config MICROBLAZE
def_bool y
select HAVE_LMB
select HAVE_FUNCTION_TRACER
+ select HAVE_FUNCTION_TRACE_MCOUNT_TEST
select USB_ARCH_HAS_EHCI
select ARCH_WANT_OPTIONAL_GPIOLIB
diff --git a/arch/microblaze/kernel/mcount.S b/arch/microblaze/kernel/mcount.S
index a257a1b..97eef3e 100644
--- a/arch/microblaze/kernel/mcount.S
+++ b/arch/microblaze/kernel/mcount.S
@@ -85,6 +85,11 @@ ENTRY(ftrace_stub)
ENTRY(_mcount)
SAVE_REGS
swi r15, r1, 0;
+ /* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST begin of checking */
+ lwi r5, r0, function_trace_stop;
+ bneid r5, end;
+ nop;
+ /* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST end of checking */
/* MS: test function trace if is taken or not */
lwi r20, r0, ftrace_trace_function;
addik r6, r0, ftrace_stub;
Signed-off-by: Mark Brown <bro...@opensource.wolfsonmicro.com>
---
drivers/power/wm8350_power.c | 2 +-
sound/soc/codecs/wm8350.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c
index 3839a5e..0693902 100644
--- a/drivers/power/wm8350_power.c
+++ b/drivers/power/wm8350_power.c
@@ -190,7 +190,7 @@ static irqreturn_t wm8350_charger_handler(int irq, void *data)
struct wm8350_power *power = &wm8350->power;
struct wm8350_charger_policy *policy = power->policy;
- switch (irq) {
+ switch (irq - wm8350->irq_base) {
case WM8350_IRQ_CHG_BAT_FAIL:
dev_err(wm8350->dev, "battery failed\n");
break;
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index f45845a..93f1e4e 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1349,7 +1349,7 @@ static irqreturn_t wm8350_hp_jack_handler(int irq, void *data)
int mask;
struct wm8350_jack_data *jack = NULL;
- switch (irq) {
+ switch (irq - wm8350->irq_base) {
case WM8350_IRQ_CODEC_JCK_DET_L:
jack = &priv->hpl;
mask = WM8350_JACK_L_LVL;
@@ -1424,7 +1424,7 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
/* Sync status */
- wm8350_hp_jack_handler(irq, priv);
+ wm8350_hp_jack_handler(irq + wm8350->irq_base, priv);
return 0;
}
--
1.6.5.7
On Wed, Jan 06, 2010 at 07:24:25PM +0000, Mark Brown wrote:
> The headphone detect and charger are using the IRQ numbers so need
> to take account of irq_base with the genirq conversion. I obviously
> picked the wrong system for initial testing.
Patch applied to my for-next branch, thanks.
Cheers,
Samuel.
--
Intel Open Source Technology Centre
http://oss.intel.com/
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 08f8a7d..d949432 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -289,6 +289,7 @@ static int option_resume(struct usb_serial *serial);
#define QUALCOMM_VENDOR_ID 0x05C6
#define CMOTECH_VENDOR_ID 0x16d8
+#define CMOTECH_PRODUCT_6280 0x6280
#define TELIT_VENDOR_ID 0x1bc7
#define TELIT_PRODUCT_UC864E 0x1003
@@ -547,7 +548,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
- { USB_DEVICE(CMOTECH_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
+ { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6280) }, /* BP3-USB & BP3-EXT HSDPA */
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
--
1.7.0.1
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 847b805..08f8a7d 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -288,7 +288,7 @@ static int option_resume(struct usb_serial *serial);
#define QUALCOMM_VENDOR_ID 0x05C6
-#define MAXON_VENDOR_ID 0x16d8
+#define CMOTECH_VENDOR_ID 0x16d8
#define TELIT_VENDOR_ID 0x1bc7
#define TELIT_PRODUCT_UC864E 0x1003
@@ -547,7 +547,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
- { USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
+ { USB_DEVICE(CMOTECH_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index d949432..f26c3c5 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -289,6 +289,7 @@ static int option_resume(struct usb_serial *serial);
#define QUALCOMM_VENDOR_ID 0x05C6
#define CMOTECH_VENDOR_ID 0x16d8
+#define CMOTECH_PRODUCT_6008 0x6008
#define CMOTECH_PRODUCT_6280 0x6280
#define TELIT_VENDOR_ID 0x1bc7
@@ -549,6 +550,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6280) }, /* BP3-USB & BP3-EXT HSDPA */
+ { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6008) },
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 08f8a7d..d949432 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -289,6 +289,7 @@ static int option_resume(struct usb_serial *serial);
#define QUALCOMM_VENDOR_ID 0x05C6
#define CMOTECH_VENDOR_ID 0x16d8
+#define CMOTECH_PRODUCT_6280 0x6280
#define TELIT_VENDOR_ID 0x1bc7
#define TELIT_PRODUCT_UC864E 0x1003
@@ -547,7 +548,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
- { USB_DEVICE(CMOTECH_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
+ { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6280) }, /* BP3-USB & BP3-EXT HSDPA */
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index d949432..f26c3c5 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -289,6 +289,7 @@ static int option_resume(struct usb_serial *serial);
#define QUALCOMM_VENDOR_ID 0x05C6
#define CMOTECH_VENDOR_ID 0x16d8
+#define CMOTECH_PRODUCT_6008 0x6008
#define CMOTECH_PRODUCT_6280 0x6280
#define TELIT_VENDOR_ID 0x1bc7
@@ -549,6 +550,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6280) }, /* BP3-USB & BP3-EXT HSDPA */
+ { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6008) },
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 847b805..08f8a7d 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -288,7 +288,7 @@ static int option_resume(struct usb_serial *serial);
#define QUALCOMM_VENDOR_ID 0x05C6
-#define MAXON_VENDOR_ID 0x16d8
+#define CMOTECH_VENDOR_ID 0x16d8
#define TELIT_VENDOR_ID 0x1bc7
#define TELIT_PRODUCT_UC864E 0x1003
@@ -547,7 +547,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
- { USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
+ { USB_DEVICE(CMOTECH_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
Adding chip specific code under quirk.
Signed-off-by: Anoop P A <anoo...@gmail.com>
---
drivers/usb/core/hub.c | 45 ++++++++++++++++++++++++++++++++++++++-----
drivers/usb/core/quirks.c | 3 ++
include/linux/usb/quirks.h | 3 ++
3 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 27115b4..4bff994 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3377,12 +3377,45 @@ static void hub_events(void)
}
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
- dev_err (hub_dev,
- "over-current change on port %d\n",
- i);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_OVER_CURRENT);
- hub_power_on(hub, true);
+ usb_detect_quirks(hdev);
+ if (hdev->quirks & USB_QUIRK_MSP_OVERCURRENT) {
+ /* clear OCC bit */
+ clear_port_feature(hdev, i,
+ USB_PORT_FEAT_C_OVER_CURRENT);
+
+ /* This step is required to toggle the
+ * PP bit to 0 and 1 (by hub_power_on)
+ * in order the CSC bit to be
+ * transitioned properly for device
+ * hotplug
+ */
+ /* clear PP bit */
+ clear_port_feature(hdev, i,
+ USB_PORT_FEAT_POWER);
+
+ /* resume power */
+ hub_power_on(hub, true);
+
+ /* delay 100 usec */
+ udelay(100);
+
+ /* read OCA bit */
+ if (portstatus &
+ (1<<USB_PORT_FEAT_OVER_CURRENT)) {
+ /* declare overcurrent */
+ dev_err(hub_dev,
+ "over-current change \
+ on port %d\n", i);
+ }
+ } else {
+ dev_err(hub_dev,
+ "over-current change \
+ on port %d\n", i);
+ clear_port_feature(hdev, i,
+ USB_PORT_FEAT_C_OVER_CURRENT);
+ hub_power_on(hub, true);
+ }
+
}
if (portchange & USB_PORT_STAT_C_RESET) {
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 25719da..59843b9 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -88,6 +88,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* INTEL VALUE SSD */
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* PMC MSP over current quirk */
+ { USB_DEVICE(0x1d6b, 0x0002), .driver_info = USB_QUIRK_MSP_OVERCURRENT },
+
{ } /* terminating entry must be last */
};
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 3e93de7..97ab168 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -30,4 +30,7 @@
descriptor */
#define USB_QUIRK_DELAY_INIT 0x00000040
+/*MSP SoC onchip EHCI overcurrent issue */
+#define USB_QUIRK_MSP_OVERCURRENT 0x00000080
+
#endif /* __LINUX_USB_QUIRKS_H */
--
1.7.0.4
> From: Anoop P A <anoo...@gmail.com>
>
> Adding chip specific code under quirk.
NAK. See below.
> Signed-off-by: Anoop P A <anoo...@gmail.com>
> ---
> drivers/usb/core/hub.c | 45 ++++++++++++++++++++++++++++++++++++++-----
> drivers/usb/core/quirks.c | 3 ++
> include/linux/usb/quirks.h | 3 ++
> 3 files changed, 45 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> index 27115b4..4bff994 100644
> --- a/drivers/usb/core/hub.c
> +++ b/drivers/usb/core/hub.c
> @@ -3377,12 +3377,45 @@ static void hub_events(void)
> }
>
> if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
> - dev_err (hub_dev,
> - "over-current change on port %d\n",
> - i);
> - clear_port_feature(hdev, i,
> - USB_PORT_FEAT_C_OVER_CURRENT);
> - hub_power_on(hub, true);
> + usb_detect_quirks(hdev);
This line is wrong. usb_detect_quirks() gets called only once per
device, when the device is initialized. Besides, you probably want to
use a hub-specific flag for this rather than a device-specific flag.
> + if (hdev->quirks & USB_QUIRK_MSP_OVERCURRENT) {
Also, it would be better to put this code in a separate subroutine
instead of indenting it so far.
This implementation is completely wrong. It applies to all USB-2.0
root hubs in Linux, not just the PMC MSP.
> { } /* terminating entry must be last */
> };
>
> diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
> index 3e93de7..97ab168 100644
> --- a/include/linux/usb/quirks.h
> +++ b/include/linux/usb/quirks.h
> @@ -30,4 +30,7 @@
> descriptor */
> #define USB_QUIRK_DELAY_INIT 0x00000040
>
> +/*MSP SoC onchip EHCI overcurrent issue */
> +#define USB_QUIRK_MSP_OVERCURRENT 0x00000080
> +
> #endif /* __LINUX_USB_QUIRKS_H */
>
--
Can you point me to an example for the recommended way of doing the
hack. I don't have much exposure to USB subsystem.
Thanks
Anoop
> > > + usb_detect_quirks(hdev);
> >
> > This line is wrong. usb_detect_quirks() gets called only once per
> > device, when the device is initialized. Besides, you probably want to
> > use a hub-specific flag for this rather than a device-specific flag.
>
> Can you point me to an example for the recommended way of doing the
> hack. I don't have much exposure to USB subsystem.
One example, suitable for PCI devices, can be found in
drivers/usb/host/ehci-pci.c:ehci_pci_setup().
However the best approach would be for you to avoid adding any
special-purpose code at all. Is it possible to handle
overcurrent-change events in a way that will work just as well for
normal hubs as for your MSP root hub?
Alan Stern
In addition to common prefix can be pre-generated, the trailing white
spaces in string constants and NULLs in char * array constants can be
removed too. These can reduce the object file size further.
The size of object file before and after changing is as follow:
text data bss dec
before: 3038 0 0 3038
after: 2118 0 0 2118
Signed-off-by: Huang Ying <ying....@intel.com>
CC: Jesse Barnes <jba...@virtuousgeek.org>
CC: Zhang Yanmin <yanmin...@intel.com>
---
drivers/pci/pcie/aer/aerdrv_errprint.c | 123 ++++++++++++---------------------
1 file changed, 48 insertions(+), 75 deletions(-)
--- a/drivers/pci/pcie/aer/aerdrv_errprint.c
+++ b/drivers/pci/pcie/aer/aerdrv_errprint.c
@@ -57,86 +57,44 @@
(e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \
AER_TRANSACTION_LAYER_ERROR)
-#define AER_PR(info, pdev, fmt, args...) \
- printk("%s%s %s: " fmt, (info->severity == AER_CORRECTABLE) ? \
- KERN_WARNING : KERN_ERR, dev_driver_string(&pdev->dev), \
- dev_name(&pdev->dev), ## args)
-
/*
* AER error strings
*/
-static char *aer_error_severity_string[] = {
+static const char * const aer_error_severity_string[] = {
"Uncorrected (Non-Fatal)",
"Uncorrected (Fatal)",
"Corrected"
};
-static char *aer_error_layer[] = {
+static const char * const aer_error_layer[] = {
"Physical Layer",
"Data Link Layer",
"Transaction Layer"
};
-static char *aer_correctable_error_string[] = {
- "Receiver Error ", /* Bit Position 0 */
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "Bad TLP ", /* Bit Position 6 */
- "Bad DLLP ", /* Bit Position 7 */
- "RELAY_NUM Rollover ", /* Bit Position 8 */
- NULL,
- NULL,
- NULL,
- "Replay Timer Timeout ", /* Bit Position 12 */
- "Advisory Non-Fatal ", /* Bit Position 13 */
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
+
+static const char * const aer_correctable_error_string[] = {
+ "Receiver Error", /* Bit Position 0 */
NULL,
NULL,
NULL,
NULL,
NULL,
+ "Bad TLP", /* Bit Position 6 */
+ "Bad DLLP", /* Bit Position 7 */
+ "RELAY_NUM Rollover", /* Bit Position 8 */
NULL,
NULL,
NULL,
+ "Replay Timer Timeout", /* Bit Position 12 */
+ "Advisory Non-Fatal", /* Bit Position 13 */
};
-static char *aer_uncorrectable_error_string[] = {
- NULL,
- NULL,
- NULL,
- NULL,
- "Data Link Protocol ", /* Bit Position 4 */
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "Poisoned TLP ", /* Bit Position 12 */
- "Flow Control Protocol ", /* Bit Position 13 */
- "Completion Timeout ", /* Bit Position 14 */
- "Completer Abort ", /* Bit Position 15 */
- "Unexpected Completion ", /* Bit Position 16 */
- "Receiver Overflow ", /* Bit Position 17 */
- "Malformed TLP ", /* Bit Position 18 */
- "ECRC ", /* Bit Position 19 */
- "Unsupported Request ", /* Bit Position 20 */
+static const char * const aer_uncorrectable_error_string[] = {
NULL,
NULL,
NULL,
NULL,
+ "Data Link Protocol", /* Bit Position 4 */
NULL,
NULL,
NULL,
@@ -144,19 +102,29 @@ static char *aer_uncorrectable_error_str
NULL,
NULL,
NULL,
+ "Poisoned TLP", /* Bit Position 12 */
+ "Flow Control Protocol", /* Bit Position 13 */
+ "Completion Timeout", /* Bit Position 14 */
+ "Completer Abort", /* Bit Position 15 */
+ "Unexpected Completion", /* Bit Position 16 */
+ "Receiver Overflow", /* Bit Position 17 */
+ "Malformed TLP", /* Bit Position 18 */
+ "ECRC", /* Bit Position 19 */
+ "Unsupported Request", /* Bit Position 20 */
};
-static char *aer_agent_string[] = {
+static const char * const aer_agent_string[] = {
"Receiver ID",
"Requester ID",
"Completer ID",
"Transmitter ID"
};
-static void __aer_print_error(struct aer_err_info *info, struct pci_dev *dev)
+static void __aer_print_error(const char *prefix,
+ struct aer_err_info *info)
{
int i, status;
- char *errmsg = NULL;
+ const char *errmsg = NULL;
status = (info->status & ~info->mask);
@@ -165,15 +133,17 @@ static void __aer_print_error(struct aer
continue;
if (info->severity == AER_CORRECTABLE)
- errmsg = aer_correctable_error_string[i];
+ errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ?
+ aer_correctable_error_string[i] : NULL;
else
- errmsg = aer_uncorrectable_error_string[i];
+ errmsg = i < ARRAY_SIZE(aer_uncorrectable_error_string) ?
+ aer_uncorrectable_error_string[i] : NULL;
if (errmsg)
- AER_PR(info, dev, " [%2d] %s%s\n", i, errmsg,
+ printk("%s"" [%2d] %-22s%s\n", prefix, i, errmsg,
info->first_error == i ? " (First)" : "");
else
- AER_PR(info, dev, " [%2d] Unknown Error Bit%s\n", i,
+ printk("%s"" [%2d] Unknown Error Bit%s\n", prefix, i,
info->first_error == i ? " (First)" : "");
}
}
@@ -181,11 +151,15 @@ static void __aer_print_error(struct aer
void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
{
int id = ((dev->bus->number << 8) | dev->devfn);
+ char prefix[44];
+
+ snprintf(prefix, sizeof(prefix), "%s%s %s: ",
+ (info->severity == AER_CORRECTABLE) ? KERN_WARNING : KERN_ERR,
+ dev_driver_string(&dev->dev), dev_name(&dev->dev));
if (info->status == 0) {
- AER_PR(info, dev,
- "PCIe Bus Error: severity=%s, type=Unaccessible, "
- "id=%04x(Unregistered Agent ID)\n",
+ printk("%s""PCIe Bus Error: severity=%s, type=Unaccessible, "
+ "id=%04x(Unregistered Agent ID)\n", prefix,
aer_error_severity_string[info->severity], id);
} else {
int layer, agent;
@@ -193,23 +167,22 @@ void aer_print_error(struct pci_dev *dev
layer = AER_GET_LAYER_ERROR(info->severity, info->status);
agent = AER_GET_AGENT(info->severity, info->status);
- AER_PR(info, dev,
- "PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n",
- aer_error_severity_string[info->severity],
+ printk("%s""PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n",
+ prefix, aer_error_severity_string[info->severity],
aer_error_layer[layer], id, aer_agent_string[agent]);
- AER_PR(info, dev,
- " device [%04x:%04x] error status/mask=%08x/%08x\n",
- dev->vendor, dev->device, info->status, info->mask);
+ printk("%s"" device [%04x:%04x] error status/mask=%08x/%08x\n",
+ prefix, dev->vendor, dev->device,
+ info->status, info->mask);
- __aer_print_error(info, dev);
+ __aer_print_error(prefix, info);
if (info->tlp_header_valid) {
unsigned char *tlp = (unsigned char *) &info->tlp;
- AER_PR(info, dev, " TLP Header:"
+ printk("%s"" TLP Header:"
" %02x%02x%02x%02x %02x%02x%02x%02x"
" %02x%02x%02x%02x %02x%02x%02x%02x\n",
- *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp,
+ prefix, *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp,
*(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4),
*(tlp + 11), *(tlp + 10), *(tlp + 9),
*(tlp + 8), *(tlp + 15), *(tlp + 14),
@@ -218,8 +191,8 @@ void aer_print_error(struct pci_dev *dev
}
if (info->id && info->error_dev_num > 1 && info->id == id)
- AER_PR(info, dev,
- " Error of this Agent(%04x) is reported first\n", id);
+ printk("%s"" Error of this Agent(%04x) is reported first\n",
+ prefix, id);
}
void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info)
The original PCIe AER error information printing function is not
re-used directly because the overall format is quite different. And
changing the original printing format may make some original users'
scripts broken.
Signed-off-by: Huang Ying <ying....@intel.com>
CC: Jesse Barnes <jba...@virtuousgeek.org>
CC: Zhang Yanmin <yanmin...@intel.com>
---
Documentation/acpi/apei/output_format.txt | 25 ++++++++++++
drivers/acpi/apei/Kconfig | 7 +++
drivers/acpi/apei/cper.c | 18 +++++++--
drivers/pci/pcie/aer/aerdrv.h | 9 ----
drivers/pci/pcie/aer/aerdrv_errprint.c | 59 ++++++++++++++++++++++++++++++
include/linux/aer.h | 24 ++++++++++++
include/linux/cper.h | 2 +
7 files changed, 132 insertions(+), 12 deletions(-)
--- a/Documentation/acpi/apei/output_format.txt
+++ b/Documentation/acpi/apei/output_format.txt
@@ -92,6 +92,11 @@ vendor_id: <integer>, device_id: <intege
class_code: <integer>]
[serial number: <integer>, <integer>]
[bridge: secondary_status: <integer>, control: <integer>]
+[aer_status: <integer>, aer_mask: <integer>
+<aer status string>
+[aer_uncor_severity: <integer>]
+aer_layer=<aer layer string>, aer_agent=<aer agent string>
+aer_tlp_header: <integer> <integer> <integer> <integer>]
<pcie port type string>* := PCIe end point | legacy PCI end point | \
unknown | unknown | root port | upstream switch port | \
@@ -99,6 +104,26 @@ downstream switch port | PCIe to PCI/PCI
PCI/PCI-X to PCIe bridge | root complex integrated endpoint device | \
root complex event collector
+if section severity is fatal or recoverable
+<aer status string># :=
+unknown | unknown | unknown | unknown | Data Link Protocol | \
+unknown | unknown | unknown | unknown | unknown | unknown | unknown | \
+Poisoned TLP | Flow Control Protocol | Completion Timeout | \
+Completer Abort | Unexpected Completion | Receiver Overflow | \
+Malformed TLP | ECRC | Unsupported Request
+else
+<aer status string># :=
+Receiver Error | unknown | unknown | unknown | unknown | unknown | \
+Bad TLP | Bad DLLP | RELAY_NUM Rollover | unknown | unknown | unknown | \
+Replay Timer Timeout | Advisory Non-Fatal
+fi
+
+<aer layer string> :=
+Physical Layer | Data Link Layer | Transaction Layer
+
+<aer agent string> :=
+Receiver ID | Requester ID | Completer ID | Transmitter ID
+
Where, [] designate corresponding content is optional
All <field string> description with * has the following format:
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -21,6 +21,13 @@ config ACPI_APEI_GHES
by firmware to produce more valuable hardware error
information for Linux.
+config ACPI_APEI_PCIEAER
+ bool "APEI PCIe AER logging/recovering support"
+ depends on ACPI_APEI && PCIEAER
+ help
+ PCIe AER errors may be reported via APEI firmware first mode.
+ Turn on this option to enable the corresponding support.
+
config ACPI_APEI_EINJ
tristate "APEI Error INJection (EINJ)"
depends on ACPI_APEI && DEBUG_FS
--- a/drivers/acpi/apei/cper.c
+++ b/drivers/acpi/apei/cper.c
@@ -29,6 +29,7 @@
#include <linux/time.h>
#include <linux/cper.h>
#include <linux/acpi.h>
+#include <linux/aer.h>
/*
* CPER record ID need to be unique even after reboot, because record
@@ -70,8 +71,8 @@ static const char *cper_severity_str(uns
* If the output length is longer than 80, multiple line will be
* printed, with @pfx is printed at the beginning of each line.
*/
-static void cper_print_bits(const char *pfx, unsigned int bits,
- const char *strs[], unsigned int strs_size)
+void cper_print_bits(const char *pfx, unsigned int bits,
+ const char *strs[], unsigned int strs_size)
{
int i, len = 0;
const char *str;
@@ -81,6 +82,8 @@ static void cper_print_bits(const char *
if (!(bits & (1U << i)))
continue;
str = strs[i];
+ if (!str)
+ continue;
if (len && len + strlen(str) + 2 > 80) {
printk("%s\n", buf);
len = 0;
@@ -243,7 +246,8 @@ static const char *cper_pcie_port_type_s
"root complex event collector",
};
-static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie)
+static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
+ const struct acpi_hest_generic_data *gdata)
{
if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
@@ -276,6 +280,12 @@ static void cper_print_pcie(const char *
printk(
"%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
pfx, pcie->bridge.secondary_status, pcie->bridge.control);
+#ifdef CONFIG_ACPI_APEI_PCIEAER
+ if (pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) {
+ struct aer_capability_regs *aer_regs = (void *)pcie->aer_info;
+ cper_print_aer(pfx, gdata->error_severity, aer_regs);
+ }
+#endif
}
static const char *apei_estatus_section_flag_strs[] = {
@@ -322,7 +332,7 @@ static void apei_estatus_print_section(
struct cper_sec_pcie *pcie = (void *)(gdata + 1);
printk("%s""section_type: PCIe error\n", pfx);
if (gdata->error_data_length >= sizeof(*pcie))
- cper_print_pcie(pfx, pcie);
+ cper_print_pcie(pfx, pcie, gdata);
else
goto err_section_too_small;
} else
--- a/drivers/pci/pcie/aer/aerdrv.h
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -35,13 +35,6 @@
PCI_ERR_UNC_UNX_COMP| \
PCI_ERR_UNC_MALF_TLP)
-struct header_log_regs {
- unsigned int dw0;
- unsigned int dw1;
- unsigned int dw2;
- unsigned int dw3;
-};
-
#define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */
struct aer_err_info {
struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES];
@@ -59,7 +52,7 @@ struct aer_err_info {
unsigned int status; /* COR/UNCOR Error Status */
unsigned int mask; /* COR/UNCOR Error Mask */
- struct header_log_regs tlp; /* TLP Header */
+ struct aer_header_log_regs tlp; /* TLP Header */
};
struct aer_err_source {
--- a/drivers/pci/pcie/aer/aerdrv_errprint.c
+++ b/drivers/pci/pcie/aer/aerdrv_errprint.c
@@ -19,6 +19,7 @@
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/suspend.h>
+#include <linux/cper.h>
#include "aerdrv.h"
@@ -201,3 +202,61 @@ void aer_print_port_info(struct pci_dev
info->multi_error_valid ? "Multiple " : "",
aer_error_severity_string[info->severity], info->id);
}
+
+#ifdef CONFIG_ACPI_APEI_PCIEAER
+static int cper_severity_to_aer(int cper_severity)
+{
+ switch (cper_severity) {
+ case CPER_SEV_RECOVERABLE:
+ return AER_NONFATAL;
+ case CPER_SEV_FATAL:
+ return AER_FATAL;
+ default:
+ return AER_CORRECTABLE;
+ }
+}
+
+void cper_print_aer(const char *prefix, int cper_severity,
+ struct aer_capability_regs *aer)
+{
+ int aer_severity, layer, agent, status_strs_size, tlp_header_valid = 0;
+ u32 status, mask;
+ const char **status_strs;
+
+ aer_severity = cper_severity_to_aer(cper_severity);
+ if (aer_severity == AER_CORRECTABLE) {
+ status = aer->cor_status;
+ mask = aer->cor_mask;
+ status_strs = aer_correctable_error_string;
+ status_strs_size = ARRAY_SIZE(aer_correctable_error_string);
+ } else {
+ status = aer->uncor_status;
+ mask = aer->uncor_mask;
+ status_strs = aer_uncorrectable_error_string;
+ status_strs_size = ARRAY_SIZE(aer_uncorrectable_error_string);
+ tlp_header_valid = status & AER_LOG_TLP_MASKS;
+ }
+ layer = AER_GET_LAYER_ERROR(aer_severity, status);
+ agent = AER_GET_AGENT(aer_severity, status);
+ printk("%s""aer_status: 0x%08x, aer_mask: 0x%08x\n",
+ prefix, status, mask);
+ cper_print_bits(prefix, status, status_strs, status_strs_size);
+ printk("%s""aer_layer=%s, aer_agent=%s\n", prefix,
+ aer_error_layer[layer], aer_agent_string[agent]);
+ if (aer_severity != AER_CORRECTABLE)
+ printk("%s""aer_uncor_severity: 0x%08x\n",
+ prefix, aer->uncor_severity);
+ if (tlp_header_valid) {
+ const unsigned char *tlp;
+ tlp = (const unsigned char *)&aer->header_log;
+ printk("%s""aer_tlp_header:"
+ " %02x%02x%02x%02x %02x%02x%02x%02x"
+ " %02x%02x%02x%02x %02x%02x%02x%02x\n",
+ prefix, *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp,
+ *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4),
+ *(tlp + 11), *(tlp + 10), *(tlp + 9),
+ *(tlp + 8), *(tlp + 15), *(tlp + 14),
+ *(tlp + 13), *(tlp + 12));
+ }
+}
+#endif
--- a/include/linux/aer.h
+++ b/include/linux/aer.h
@@ -7,6 +7,28 @@
#ifndef _AER_H_
#define _AER_H_
+struct aer_header_log_regs {
+ unsigned int dw0;
+ unsigned int dw1;
+ unsigned int dw2;
+ unsigned int dw3;
+};
+
+struct aer_capability_regs {
+ u32 header;
+ u32 uncor_status;
+ u32 uncor_mask;
+ u32 uncor_severity;
+ u32 cor_status;
+ u32 cor_mask;
+ u32 cap_control;
+ struct aer_header_log_regs header_log;
+ u32 root_command;
+ u32 root_status;
+ u16 cor_err_source;
+ u16 uncor_err_source;
+};
+
#if defined(CONFIG_PCIEAER)
/* pci-e port driver needs this function to enable aer */
extern int pci_enable_pcie_error_reporting(struct pci_dev *dev);
@@ -27,5 +49,7 @@ static inline int pci_cleanup_aer_uncorr
}
#endif
+extern void cper_print_aer(const char *prefix, int cper_severity,
+ struct aer_capability_regs *aer);
#endif //_AER_H_
--- a/include/linux/cper.h
+++ b/include/linux/cper.h
@@ -388,5 +388,7 @@ struct cper_sec_pcie {
#pragma pack()
u64 cper_next_record_id(void);
+void cper_print_bits(const char *prefix, unsigned int bits,
+ const char *strs[], unsigned int strs_size);
#endif
Regards
Pratyush
On 2/21/2011 1:43 PM, y wrote:
> From: Pratyush Anand <pratyus...@st.com>
>
> This is a configurable gadget. can be configured by configfs interface. Any
> IP available at PCIE bus can be programmed to be used by host
> controller.It supoorts both INTX and MSI.
> By default, gadget is configured for INTX and SYSRAM1 is mapped to BAR0
> with size 0x1000
>
> Changes since V4:
> - All documentation related comments incorporated
>
> Changes since V3:
> - support for multiple instances of such device
> - changes to minimzie portability issue on 64 bit machine
> - unnecessary typecast removed
> - sysfs_streq used in place of complex code
>
> Changes since V2:
> - driver has been moved from sysfs to configfs
> - Documentation/ABI directory has also been updated
> - typo error in documenation has been corrected
> - clk value is checked after encapsulating by IS_ERR
>
> Changes since V1:
> - __iomem added for register addresses
> - kerneldoc comment removed whereever not required.
> - help node moved from sysfs to documentation/misc-devices
> - strict_strtoul used instead of sscanf
>
> Signed-off-by: Pratyush Anand <pratyus...@st.com>
> ---
> .../ABI/testing/configfs-spear-pcie-gadget | 30 +
> Documentation/misc-devices/spear-pcie-gadget.txt | 129 +++
> drivers/misc/Kconfig | 10 +
> drivers/misc/Makefile | 1 +
> drivers/misc/spear13xx_pcie_gadget.c | 908 ++++++++++++++++++++
> 5 files changed, 1078 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/ABI/testing/configfs-spear-pcie-gadget
> create mode 100644 Documentation/misc-devices/spear-pcie-gadget.txt
> create mode 100644 drivers/misc/spear13xx_pcie_gadget.c
>
> diff --git a/Documentation/ABI/testing/configfs-spear-pcie-gadget b/Documentation/ABI/testing/configfs-spear-pcie-gadget
> new file mode 100644
> index 0000000..29593d0
> --- /dev/null
> +++ b/Documentation/ABI/testing/configfs-spear-pcie-gadget
> @@ -0,0 +1,30 @@
> +What: /config/pcie-gadget
> +Date: Feb 2011
> +KernelVersion: 2.6.37
> +Contact: Pratyush Anand <pratyus...@st.com>
> +Description:
> +
> + Interface is used to configure selected dual mode PCIe controller
> + as device and then program its various registers to configure it
> + as a particular device type.
> + This interfaces can be used to show spear's PCIe device capability.
> +
> + Nodes are only visible when configfs is mounted. To mount configfs
> + in /config directory use:
> + # mount -t configfs none /config/
> +
> + /config/pcie-gadget/
> + link ... used to enable ltssm and read its status.
> + int_type ...used to configure and read type of supported
> + interrupt
> + no_of_msi ... used to configure number of MSI vector needed and
> + to read no of MSI granted.
> + inta ... write 1 to assert INTA and 0 to de-assert.
> + send_msi ... write MSI vector to be sent.
> + vendor_id ... used to write and read vendor id (hex)
> + device_id ... used to write and read device id (hex)
> + bar0_size ... used to write and read bar0_size
> + bar0_address ... used to write and read bar0 mapped area in hex.
> + bar0_rw_offset ... used to write and read offset of bar0 where
> + bar0_data will be written or read.
> + bar0_data ... used to write and read data at bar0_rw_offset.
> diff --git a/Documentation/misc-devices/spear-pcie-gadget.txt b/Documentation/misc-devices/spear-pcie-gadget.txt
> new file mode 100644
> index 0000000..7b86b80
> --- /dev/null
> +++ b/Documentation/misc-devices/spear-pcie-gadget.txt
> @@ -0,0 +1,129 @@
> +Spear PCIe Gadget Driver:
> +
> +Author
> +=============
> +Pratyush Anand (pratyus...@st.com)
> +
> +Location
> +============
> +driver/misc/spear13xx_pcie_gadget.c
> +
> +Supported Chip:
> +===================
> +SPEAr1300
> +SPEAr1310
> +
> +Menuconfig option:
> +==========================
> +Device Drivers
> + Misc devices
> + PCIe gadget support for SPEAr13XX platform
> +purpose
> +===========
> +This driver has several nodes which can be read/written by configfs interface.
> +Its main purpose is to configure selected dual mode PCIe controller as device
> +and then program its various registers to configure it as a particular device
> +type. This driver can be used to show spear's PCIe device capability.
> +
> +Description of different nodes:
> +=================================
> +
> +read behavior of nodes:
> +------------------------------
> +link :gives ltssm status.
> +int_type :type of supported interrupt
> +no_of_msi :zero if MSI is not enabled by host. A positive value is the
> + number of MSI vector granted.
> +vendor_id :returns programmed vendor id (hex)
> +device_id :returns programmed device id(hex)
> +bar0_size: :returns size of bar0 in hex.
> +bar0_address :returns address of bar0 mapped area in hex.
> +bar0_rw_offset :returns offset of bar0 for which bar0_data will return value.
> +bar0_data :returns data at bar0_rw_offset.
> +
> +write behavior of nodes:
> +------------------------------
> +link :write UP to enable ltsmm DOWN to disable
> +int_type :write interrupt type to be configured and (int_type could be
> + INTA, MSI or NO_INT). Select MSI only when you have programmed
> + no_of_msi node.
> +no_of_msi :number of MSI vector needed.
> +inta :write 1 to assert INTA and 0 to de-assert.
> +send_msi :write MSI vector to be sent.
> +vendor_id :write vendor id(hex) to be programmed.
> +device_id :write device id(hex) to be programmed.
> +bar0_size :write size of bar0 in hex. default bar0 size is 1000 (hex)
> + bytes.
> +bar0_address :write address of bar0 mapped area in hex. (default mapping of
> + bar0 is SYSRAM1(E0800000). Always program bar size before bar
> + address. Kernel might modify bar size and address for alignment, so
> + read back bar size and address after writing to cross check.
> +bar0_rw_offset :write offset of bar0 for which bar0_data will write value.
> +bar0_data :write data to be written at bar0_rw_offset.
> +
> +Node programming example
> +===========================
> +Program all PCIe registers in such a way that when this device is connected
> +to the PCIe host, then host sees this device as 1MB RAM.
> +#mount -t configfs none /Config
> +# cd /config/pcie_gadget/
> +Now you have all the nodes in this directory.
> +program vendor id as 0x104a
> +# echo 104A >> vendor_id
> +
> +program device id as 0xCD80
> +# echo CD80 >> device_id
> +
> +program BAR0 size as 1MB
> +# echo 100000 >> bar0_size
> +
> +check for programmed bar0 size
> +# cat bar0_size
> +
> +Program BAR0 Address as DDR (0x2100000). This is the physical address of
> +memory, which is to be made visible to PCIe host. Similarly any other peripheral
> +can also be made visible to PCIe host. E.g., if you program base address of UART
> +as BAR0 address then when this device will be connected to a host, it will be
> +visible as UART.
> +# echo 2100000 >> bar0_address
> +
> +program interrupt type : INTA
> +# echo INTA >> int_type
> +
> +go for link up now.
> +# echo UP >> link
> +
> +It will have to be insured that, once link up is done on gadget, then only host
> +is initialized and start to search PCIe devices on its port.
> +
> +/*wait till link is up*/
> +# cat link
> +wait till it returns UP.
> +
> +To assert INTA
> +# echo 1 >> inta
> +
> +To de-assert INTA
> +# echo 0 >> inta
> +
> +if MSI is to be used as interrupt, program no of msi vector needed (say4)
> +# echo 4 >> no_of_msi
> +
> +select MSI as interrupt type
> +# echo MSI >> int_type
> +
> +go for link up now
> +# echo UP >> link
> +
> +wait till link is up
> +# cat link
> +An application can repetitively read this node till link is found UP. It can
> +sleep between two read.
> +
> +wait till msi is enabled
> +# cat no_of_msi
> +Should return 4 (number of requested MSI vector)
> +
> +to send msi vector 2
> +# echo 2 >> send_msi
> +#cd -
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index 4d073f1..dea052d 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -394,6 +394,16 @@ config DS1682
> This driver can also be built as a module. If so, the module
> will be called ds1682.
>
> +config SPEAR13XX_PCIE_GADGET
> + bool "PCIe gadget support for SPEAr13XX platform"
> + depends on ARCH_SPEAR13XX
> + default n
> + help
> + This option enables gadget support for PCIe controller. If
> + board file defines any controller as PCIe endpoint then a sysfs
> + entry will be created for that controller. User can use these
> + sysfs node to configure PCIe EP as per his requirements.
> +
> config TI_DAC7512
> tristate "Texas Instruments DAC7512"
> depends on SPI && SYSFS
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index 98009cc..c489536 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
> obj-$(CONFIG_HMC6352) += hmc6352.o
> obj-y += eeprom/
> obj-y += cb710/
> +obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o
> obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o
> obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
> obj-$(CONFIG_PCH_PHUB) += pch_phub.o
> diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c
> new file mode 100644
> index 0000000..ec3b8c9
> --- /dev/null
> +++ b/drivers/misc/spear13xx_pcie_gadget.c
> @@ -0,0 +1,908 @@
> +/*
> + * drivers/misc/spear13xx_pcie_gadget.c
> + *
> + * Copyright (C) 2010 ST Microelectronics
> + * Pratyush Anand<pratyus...@st.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pci_regs.h>
> +#include <linux/configfs.h>
> +#include <mach/pcie.h>
> +#include <mach/misc_regs.h>
> +
> +#define IN0_MEM_SIZE (200 * 1024 * 1024 - 1)
> +/* In current implementation address translation is done using IN0 only.
> + * So IN1 start address and IN0 end address has been kept same
> +*/
> +#define IN1_MEM_SIZE (0 * 1024 * 1024 - 1)
> +#define IN_IO_SIZE (20 * 1024 * 1024 - 1)
> +#define IN_CFG0_SIZE (12 * 1024 * 1024 - 1)
> +#define IN_CFG1_SIZE (12 * 1024 * 1024 - 1)
> +#define IN_MSG_SIZE (12 * 1024 * 1024 - 1)
> +/* Keep default BAR size as 4K*/
> +/* AORAM would be mapped by default*/
> +#define INBOUND_ADDR_MASK (SPEAR13XX_SYSRAM1_SIZE - 1)
> +
> +#define INT_TYPE_NO_INT 0
> +#define INT_TYPE_INTX 1
> +#define INT_TYPE_MSI 2
> +struct spear_pcie_gadget_config {
> + void __iomem *base;
> + void __iomem *va_app_base;
> + void __iomem *va_dbi_base;
> + char int_type[10];
> + ulong requested_msi;
> + ulong configured_msi;
> + ulong bar0_size;
> + ulong bar0_rw_offset;
> + void __iomem *va_bar0_address;
> +};
> +
> +struct pcie_gadget_target {
> + struct configfs_subsystem subsys;
> + struct spear_pcie_gadget_config config;
> +};
> +
> +struct pcie_gadget_target_attr {
> + struct configfs_attribute attr;
> + ssize_t (*show)(struct spear_pcie_gadget_config *config,
> + char *buf);
> + ssize_t (*store)(struct spear_pcie_gadget_config *config,
> + const char *buf,
> + size_t count);
> +};
> +
> +static void enable_dbi_access(struct pcie_app_reg __iomem *app_reg)
> +{
> + /* Enable DBI access */
> + writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID),
> + &app_reg->slv_armisc);
> + writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID),
> + &app_reg->slv_awmisc);
> +
> +}
> +
> +static void disable_dbi_access(struct pcie_app_reg __iomem *app_reg)
> +{
> + /* disable DBI access */
> + writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
> + &app_reg->slv_armisc);
> + writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
> + &app_reg->slv_awmisc);
> +
> +}
> +
> +static void spear_dbi_read_reg(struct spear_pcie_gadget_config *config,
> + int where, int size, u32 *val)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> + ulong va_address;
> +
> + /* Enable DBI access */
> + enable_dbi_access(app_reg);
> +
> + va_address = (ulong)config->va_dbi_base + (where & ~0x3);
> +
> + *val = readl(va_address);
> +
> + if (size == 1)
> + *val = (*val >> (8 * (where & 3))) & 0xff;
> + else if (size == 2)
> + *val = (*val >> (8 * (where & 3))) & 0xffff;
> +
> + /* Disable DBI access */
> + disable_dbi_access(app_reg);
> +}
> +
> +static void spear_dbi_write_reg(struct spear_pcie_gadget_config *config,
> + int where, int size, u32 val)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> + ulong va_address;
> +
> + /* Enable DBI access */
> + enable_dbi_access(app_reg);
> +
> + va_address = (ulong)config->va_dbi_base + (where & ~0x3);
> +
> + if (size == 4)
> + writel(val, va_address);
> + else if (size == 2)
> + writew(val, va_address + (where & 2));
> + else if (size == 1)
> + writeb(val, va_address + (where & 3));
> +
> + /* Disable DBI access */
> + disable_dbi_access(app_reg);
> +}
> +
> +#define PCI_FIND_CAP_TTL 48
> +
> +static int pci_find_own_next_cap_ttl(struct spear_pcie_gadget_config *config,
> + u32 pos, int cap, int *ttl)
> +{
> + u32 id;
> +
> + while ((*ttl)--) {
> + spear_dbi_read_reg(config, pos, 1, &pos);
> + if (pos < 0x40)
> + break;
> + pos &= ~3;
> + spear_dbi_read_reg(config, pos + PCI_CAP_LIST_ID, 1, &id);
> + if (id == 0xff)
> + break;
> + if (id == cap)
> + return pos;
> + pos += PCI_CAP_LIST_NEXT;
> + }
> + return 0;
> +}
> +
> +static int pci_find_own_next_cap(struct spear_pcie_gadget_config *config,
> + u32 pos, int cap)
> +{
> + int ttl = PCI_FIND_CAP_TTL;
> +
> + return pci_find_own_next_cap_ttl(config, pos, cap, &ttl);
> +}
> +
> +static int pci_find_own_cap_start(struct spear_pcie_gadget_config *config,
> + u8 hdr_type)
> +{
> + u32 status;
> +
> + spear_dbi_read_reg(config, PCI_STATUS, 2, &status);
> + if (!(status & PCI_STATUS_CAP_LIST))
> + return 0;
> +
> + switch (hdr_type) {
> + case PCI_HEADER_TYPE_NORMAL:
> + case PCI_HEADER_TYPE_BRIDGE:
> + return PCI_CAPABILITY_LIST;
> + case PCI_HEADER_TYPE_CARDBUS:
> + return PCI_CB_CAPABILITY_LIST;
> + default:
> + return 0;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Tell if a device supports a given PCI capability.
> + * Returns the address of the requested capability structure within the
> + * device's PCI configuration space or 0 in case the device does not
> + * support it. Possible values for @cap:
> + *
> + * %PCI_CAP_ID_PM Power Management
> + * %PCI_CAP_ID_AGP Accelerated Graphics Port
> + * %PCI_CAP_ID_VPD Vital Product Data
> + * %PCI_CAP_ID_SLOTID Slot Identification
> + * %PCI_CAP_ID_MSI Message Signalled Interrupts
> + * %PCI_CAP_ID_CHSWP CompactPCI HotSwap
> + * %PCI_CAP_ID_PCIX PCI-X
> + * %PCI_CAP_ID_EXP PCI Express
> + */
> +static int pci_find_own_capability(struct spear_pcie_gadget_config *config,
> + int cap)
> +{
> + u32 pos;
> + u32 hdr_type;
> +
> + spear_dbi_read_reg(config, PCI_HEADER_TYPE, 1, &hdr_type);
> +
> + pos = pci_find_own_cap_start(config, hdr_type);
> + if (pos)
> + pos = pci_find_own_next_cap(config, pos, cap);
> +
> + return pos;
> +}
> +
> +static irqreturn_t spear_pcie_gadget_irq(int irq, void *dev_id)
> +{
> + return 0;
> +}
> +
> +/*
> + * configfs interfaces show/store functions
> + */
> +static ssize_t pcie_gadget_show_link(
> + struct spear_pcie_gadget_config *config,
> + char *buf)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> +
> + if (readl(&app_reg->app_status_1) & ((u32)1 << XMLH_LINK_UP_ID))
> + return sprintf(buf, "UP");
> + else
> + return sprintf(buf, "DOWN");
> +}
> +
> +static ssize_t pcie_gadget_store_link(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> +
> + if (sysfs_streq(buf, "UP"))
> + writel(readl(&app_reg->app_ctrl_0) | (1 << APP_LTSSM_ENABLE_ID),
> + &app_reg->app_ctrl_0);
> + else if (sysfs_streq(buf, "DOWN"))
> + writel(readl(&app_reg->app_ctrl_0)
> + & ~(1 << APP_LTSSM_ENABLE_ID),
> + &app_reg->app_ctrl_0);
> + else
> + return -EINVAL;
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_show_int_type(
> + struct spear_pcie_gadget_config *config,
> + char *buf)
> +{
> + return sprintf(buf, "%s", config->int_type);
> +}
> +
> +static ssize_t pcie_gadget_store_int_type(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + u32 cap, vec, flags;
> + ulong vector;
> +
> + if (sysfs_streq(buf, "INTA"))
> + spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
> +
> + else if (sysfs_streq(buf, "MSI")) {
> + vector = config->requested_msi;
> + vec = 0;
> + while (vector > 1) {
> + vector /= 2;
> + vec++;
> + }
> + spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 0);
> + cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
> + spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
> + flags &= ~PCI_MSI_FLAGS_QMASK;
> + flags |= vec << 1;
> + spear_dbi_write_reg(config, cap + PCI_MSI_FLAGS, 1, flags);
> + } else
> + return -EINVAL;
> +
> + strcpy(config->int_type, buf);
> +
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_show_no_of_msi(
> + struct spear_pcie_gadget_config *config,
> + char *buf)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> + u32 cap, vec, flags;
> + ulong vector;
> +
> + if ((readl(&app_reg->msg_status) & (1 << CFG_MSI_EN_ID))
> + != (1 << CFG_MSI_EN_ID))
> + vector = 0;
> + else {
> + cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
> + spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
> + flags &= ~PCI_MSI_FLAGS_QSIZE;
> + vec = flags >> 4;
> + vector = 1;
> + while (vec--)
> + vector *= 2;
> + }
> + config->configured_msi = vector;
> +
> + return sprintf(buf, "%lu", vector);
> +}
> +
> +static ssize_t pcie_gadget_store_no_of_msi(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + if (strict_strtoul(buf, 0, &config->requested_msi))
> + return -EINVAL;
> + if (config->requested_msi > 32)
> + config->requested_msi = 32;
> +
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_store_inta(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> + ulong en;
> +
> + if (strict_strtoul(buf, 0, &en))
> + return -EINVAL;
> +
> + if (en)
> + writel(readl(&app_reg->app_ctrl_0) | (1 << SYS_INT_ID),
> + &app_reg->app_ctrl_0);
> + else
> + writel(readl(&app_reg->app_ctrl_0) & ~(1 << SYS_INT_ID),
> + &app_reg->app_ctrl_0);
> +
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_store_send_msi(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> + ulong vector;
> + u32 ven_msi;
> +
> + if (strict_strtoul(buf, 0, &vector))
> + return -EINVAL;
> +
> + if (!config->configured_msi)
> + return -EINVAL;
> +
> + if (vector >= config->configured_msi)
> + return -EINVAL;
> +
> + ven_msi = readl(&app_reg->ven_msi_1);
> + ven_msi &= ~VEN_MSI_FUN_NUM_MASK;
> + ven_msi |= 0 << VEN_MSI_FUN_NUM_ID;
> + ven_msi &= ~VEN_MSI_TC_MASK;
> + ven_msi |= 0 << VEN_MSI_TC_ID;
> + ven_msi &= ~VEN_MSI_VECTOR_MASK;
> + ven_msi |= vector << VEN_MSI_VECTOR_ID;
> +
> + /* generating interrupt for msi vector */
> + ven_msi |= VEN_MSI_REQ_EN;
> + writel(ven_msi, &app_reg->ven_msi_1);
> + udelay(1);
> + ven_msi &= ~VEN_MSI_REQ_EN;
> + writel(ven_msi, &app_reg->ven_msi_1);
> +
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_show_vendor_id(
> + struct spear_pcie_gadget_config *config,
> + char *buf)
> +{
> + u32 id;
> +
> + spear_dbi_read_reg(config, PCI_VENDOR_ID, 2, &id);
> +
> + return sprintf(buf, "%x", id);
> +}
> +
> +static ssize_t pcie_gadget_store_vendor_id(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + ulong id;
> +
> + if (strict_strtoul(buf, 0, &id))
> + return -EINVAL;
> +
> + spear_dbi_write_reg(config, PCI_VENDOR_ID, 2, id);
> +
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_show_device_id(
> + struct spear_pcie_gadget_config *config,
> + char *buf)
> +{
> + u32 id;
> +
> + spear_dbi_read_reg(config, PCI_DEVICE_ID, 2, &id);
> +
> + return sprintf(buf, "%x", id);
> +}
> +
> +static ssize_t pcie_gadget_store_device_id(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + ulong id;
> +
> + if (strict_strtoul(buf, 0, &id))
> + return -EINVAL;
> +
> + spear_dbi_write_reg(config, PCI_DEVICE_ID, 2, id);
> +
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_show_bar0_size(
> + struct spear_pcie_gadget_config *config,
> + char *buf)
> +{
> + return sprintf(buf, "%lx", config->bar0_size);
> +}
> +
> +static ssize_t pcie_gadget_store_bar0_size(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + ulong size;
> + u32 pos, pos1;
> + u32 no_of_bit = 0;
> +
> + if (strict_strtoul(buf, 0, &size))
> + return -EINVAL;
> + /* min bar size is 256 */
> + if (size <= 0x100)
> + size = 0x100;
> + /* max bar size is 1MB*/
> + else if (size >= 0x100000)
> + size = 0x100000;
> + else {
> + pos = 0;
> + pos1 = 0;
> + while (pos < 21) {
> + pos = find_next_bit((ulong *)&size, 21, pos);
> + if (pos != 21)
> + pos1 = pos + 1;
> + pos++;
> + no_of_bit++;
> + }
> + if (no_of_bit == 2)
> + pos1--;
> +
> + size = 1 << pos1;
> + }
> + config->bar0_size = size;
> + spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, size - 1);
> +
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_show_bar0_address(
> + struct spear_pcie_gadget_config *config,
> + char *buf)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> +
> + u32 address = readl(&app_reg->pim0_mem_addr_start);
> +
> + return sprintf(buf, "%x", address);
> +}
> +
> +static ssize_t pcie_gadget_store_bar0_address(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> + ulong address;
> +
> + if (strict_strtoul(buf, 0, &address))
> + return -EINVAL;
> +
> + address &= ~(config->bar0_size - 1);
> + if (config->va_bar0_address)
> + iounmap(config->va_bar0_address);
> + config->va_bar0_address = ioremap(address, config->bar0_size);
> + if (!config->va_bar0_address)
> + return -ENOMEM;
> +
> + writel(address, &app_reg->pim0_mem_addr_start);
> +
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_show_bar0_rw_offset(
> + struct spear_pcie_gadget_config *config,
> + char *buf)
> +{
> + return sprintf(buf, "%lx", config->bar0_rw_offset);
> +}
> +
> +static ssize_t pcie_gadget_store_bar0_rw_offset(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + ulong offset;
> +
> + if (strict_strtoul(buf, 0, &offset))
> + return -EINVAL;
> +
> + if (offset % 4)
> + return -EINVAL;
> +
> + config->bar0_rw_offset = offset;
> +
> + return count;
> +}
> +
> +static ssize_t pcie_gadget_show_bar0_data(
> + struct spear_pcie_gadget_config *config,
> + char *buf)
> +{
> + ulong data;
> +
> + if (!config->va_bar0_address)
> + return -ENOMEM;
> +
> + data = readl((ulong)config->va_bar0_address + config->bar0_rw_offset);
> +
> + return sprintf(buf, "%lx", data);
> +}
> +
> +static ssize_t pcie_gadget_store_bar0_data(
> + struct spear_pcie_gadget_config *config,
> + const char *buf, size_t count)
> +{
> + ulong data;
> +
> + if (strict_strtoul(buf, 0, &data))
> + return -EINVAL;
> +
> + if (!config->va_bar0_address)
> + return -ENOMEM;
> +
> + writel(data, (ulong)config->va_bar0_address + config->bar0_rw_offset);
> +
> + return count;
> +}
> +
> +/*
> + * Attribute definitions.
> + */
> +
> +#define PCIE_GADGET_TARGET_ATTR_RO(_name) \
> +static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
> + __CONFIGFS_ATTR(_name, S_IRUGO, pcie_gadget_show_##_name, NULL)
> +
> +#define PCIE_GADGET_TARGET_ATTR_WO(_name) \
> +static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
> + __CONFIGFS_ATTR(_name, S_IWUSR, NULL, pcie_gadget_store_##_name)
> +
> +#define PCIE_GADGET_TARGET_ATTR_RW(_name) \
> +static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
> + __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, pcie_gadget_show_##_name, \
> + pcie_gadget_store_##_name)
> +PCIE_GADGET_TARGET_ATTR_RW(link);
> +PCIE_GADGET_TARGET_ATTR_RW(int_type);
> +PCIE_GADGET_TARGET_ATTR_RW(no_of_msi);
> +PCIE_GADGET_TARGET_ATTR_WO(inta);
> +PCIE_GADGET_TARGET_ATTR_WO(send_msi);
> +PCIE_GADGET_TARGET_ATTR_RW(vendor_id);
> +PCIE_GADGET_TARGET_ATTR_RW(device_id);
> +PCIE_GADGET_TARGET_ATTR_RW(bar0_size);
> +PCIE_GADGET_TARGET_ATTR_RW(bar0_address);
> +PCIE_GADGET_TARGET_ATTR_RW(bar0_rw_offset);
> +PCIE_GADGET_TARGET_ATTR_RW(bar0_data);
> +
> +static struct configfs_attribute *pcie_gadget_target_attrs[] = {
> + &pcie_gadget_target_link.attr,
> + &pcie_gadget_target_int_type.attr,
> + &pcie_gadget_target_no_of_msi.attr,
> + &pcie_gadget_target_inta.attr,
> + &pcie_gadget_target_send_msi.attr,
> + &pcie_gadget_target_vendor_id.attr,
> + &pcie_gadget_target_device_id.attr,
> + &pcie_gadget_target_bar0_size.attr,
> + &pcie_gadget_target_bar0_address.attr,
> + &pcie_gadget_target_bar0_rw_offset.attr,
> + &pcie_gadget_target_bar0_data.attr,
> + NULL,
> +};
> +
> +static struct pcie_gadget_target *to_target(struct config_item *item)
> +{
> + return item ?
> + container_of(to_configfs_subsystem(to_config_group(item)),
> + struct pcie_gadget_target, subsys) : NULL;
> +}
> +
> +/*
> + * Item operations and type for pcie_gadget_target.
> + */
> +
> +static ssize_t pcie_gadget_target_attr_show(struct config_item *item,
> + struct configfs_attribute *attr,
> + char *buf)
> +{
> + ssize_t ret = -EINVAL;
> + struct pcie_gadget_target *target = to_target(item);
> + struct pcie_gadget_target_attr *t_attr =
> + container_of(attr, struct pcie_gadget_target_attr, attr);
> +
> + if (t_attr->show)
> + ret = t_attr->show(&target->config, buf);
> + return ret;
> +}
> +
> +static ssize_t pcie_gadget_target_attr_store(struct config_item *item,
> + struct configfs_attribute *attr,
> + const char *buf,
> + size_t count)
> +{
> + ssize_t ret = -EINVAL;
> + struct pcie_gadget_target *target = to_target(item);
> + struct pcie_gadget_target_attr *t_attr =
> + container_of(attr, struct pcie_gadget_target_attr, attr);
> +
> + if (t_attr->store)
> + ret = t_attr->store(&target->config, buf, count);
> + return ret;
> +}
> +
> +static struct configfs_item_operations pcie_gadget_target_item_ops = {
> + .show_attribute = pcie_gadget_target_attr_show,
> + .store_attribute = pcie_gadget_target_attr_store,
> +};
> +
> +static struct config_item_type pcie_gadget_target_type = {
> + .ct_attrs = pcie_gadget_target_attrs,
> + .ct_item_ops = &pcie_gadget_target_item_ops,
> + .ct_owner = THIS_MODULE,
> +};
> +
> +static void spear13xx_pcie_device_init(struct spear_pcie_gadget_config *config)
> +{
> + struct pcie_app_reg __iomem *app_reg = config->va_app_base;
> +
> + /*setup registers for outbound translation */
> +
> + writel(config->base, &app_reg->in0_mem_addr_start);
> + writel(app_reg->in0_mem_addr_start + IN0_MEM_SIZE,
> + &app_reg->in0_mem_addr_limit);
> + writel(app_reg->in0_mem_addr_limit + 1, &app_reg->in1_mem_addr_start);
> + writel(app_reg->in1_mem_addr_start + IN1_MEM_SIZE,
> + &app_reg->in1_mem_addr_limit);
> + writel(app_reg->in1_mem_addr_limit + 1, &app_reg->in_io_addr_start);
> + writel(app_reg->in_io_addr_start + IN_IO_SIZE,
> + &app_reg->in_io_addr_limit);
> + writel(app_reg->in_io_addr_limit + 1, &app_reg->in_cfg0_addr_start);
> + writel(app_reg->in_cfg0_addr_start + IN_CFG0_SIZE,
> + &app_reg->in_cfg0_addr_limit);
> + writel(app_reg->in_cfg0_addr_limit + 1, &app_reg->in_cfg1_addr_start);
> + writel(app_reg->in_cfg1_addr_start + IN_CFG1_SIZE,
> + &app_reg->in_cfg1_addr_limit);
> + writel(app_reg->in_cfg1_addr_limit + 1, &app_reg->in_msg_addr_start);
> + writel(app_reg->in_msg_addr_start + IN_MSG_SIZE,
> + &app_reg->in_msg_addr_limit);
> +
> + writel(app_reg->in0_mem_addr_start, &app_reg->pom0_mem_addr_start);
> + writel(app_reg->in1_mem_addr_start, &app_reg->pom1_mem_addr_start);
> + writel(app_reg->in_io_addr_start, &app_reg->pom_io_addr_start);
> +
> + /*setup registers for inbound translation */
> +
> + /* Keep AORAM mapped at BAR0 as default */
> + config->bar0_size = INBOUND_ADDR_MASK + 1;
> + spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, INBOUND_ADDR_MASK);
> + spear_dbi_write_reg(config, PCI_BASE_ADDRESS_0, 4, 0xC);
> + config->va_bar0_address = ioremap(SPEAR13XX_SYSRAM1_BASE,
> + config->bar0_size);
> +
> + writel(SPEAR13XX_SYSRAM1_BASE, &app_reg->pim0_mem_addr_start);
> + writel(0, &app_reg->pim1_mem_addr_start);
> + writel(INBOUND_ADDR_MASK + 1, &app_reg->mem0_addr_offset_limit);
> +
> + writel(0x0, &app_reg->pim_io_addr_start);
> + writel(0x0, &app_reg->pim_io_addr_start);
> + writel(0x0, &app_reg->pim_rom_addr_start);
> +
> + writel(DEVICE_TYPE_EP | (1 << MISCTRL_EN_ID)
> + | ((u32)1 << REG_TRANSLATION_ENABLE),
> + &app_reg->app_ctrl_0);
> + /* disable all rx interrupts */
> + writel(0, &app_reg->int_mask);
> +
> + /* Select INTA as default*/
> + spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
> +}
> +
> +static int __devinit spear_pcie_gadget_probe(struct platform_device *pdev)
> +{
> + struct resource *res0, *res1;
> + unsigned int status = 0;
> + int irq;
> + struct clk *clk;
> + static struct pcie_gadget_target *target;
> + struct spear_pcie_gadget_config *config;
> + struct config_item *cg_item;
> + struct configfs_subsystem *subsys;
> +
> + /* get resource for application registers*/
> +
> + res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res0) {
> + dev_err(&pdev->dev, "no resource defined\n");
> + return -EBUSY;
> + }
> + if (!request_mem_region(res0->start, resource_size(res0),
> + pdev->name)) {
> + dev_err(&pdev->dev, "pcie gadget region already claimed\n");
> + return -EBUSY;
> + }
> + /* get resource for dbi registers*/
> +
> + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + if (!res1) {
> + dev_err(&pdev->dev, "no resource defined\n");
> + goto err_rel_res0;
> + }
> + if (!request_mem_region(res1->start, resource_size(res1),
> + pdev->name)) {
> + dev_err(&pdev->dev, "pcie gadget region already claimed\n");
> + goto err_rel_res0;
> + }
> +
> + target = kzalloc(sizeof(*target), GFP_KERNEL);
> + if (!target) {
> + dev_err(&pdev->dev, "out of memory\n");
> + status = -ENOMEM;
> + goto err_rel_res;
> + }
> +
> + cg_item = &target->subsys.su_group.cg_item;
> + sprintf(cg_item->ci_namebuf, "pcie_gadget.%d", pdev->id);
> + cg_item->ci_type = &pcie_gadget_target_type;
> + config = &target->config;
> + config->va_app_base = (void __iomem *)ioremap(res0->start,
> + resource_size(res0));
> + if (!config->va_app_base) {
> + dev_err(&pdev->dev, "ioremap fail\n");
> + status = -ENOMEM;
> + goto err_kzalloc;
> + }
> +
> + config->base = (void __iomem *)res1->start;
> +
> + config->va_dbi_base = (void __iomem *)ioremap(res1->start,
> + resource_size(res1));
> + if (!config->va_dbi_base) {
> + dev_err(&pdev->dev, "ioremap fail\n");
> + status = -ENOMEM;
> + goto err_iounmap_app;
> + }
> +
> + dev_set_drvdata(&pdev->dev, target);
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_err(&pdev->dev, "no update irq?\n");
> + status = irq;
> + goto err_iounmap;
> + }
> +
> + status = request_irq(irq, spear_pcie_gadget_irq, 0, pdev->name, NULL);
> + if (status) {
> + dev_err(&pdev->dev, "pcie gadget interrupt IRQ%d already \
> + claimed\n", irq);
> + goto err_iounmap;
> + }
> +
> + /* Register configfs hooks */
> + subsys = &target->subsys;
> + config_group_init(&subsys->su_group);
> + mutex_init(&subsys->su_mutex);
> + status = configfs_register_subsystem(subsys);
> + if (status)
> + goto err_irq;
> +
> + /*
> + * init basic pcie application registers
> + * do not enable clock if it is PCIE0.Ideally , all controller should
> + * have been independent from others with respect to clock. But PCIE1
> + * and 2 depends on PCIE0.So PCIE0 clk is provided during board init.
> + */
> + if (pdev->id == 1) {
> + /*
> + * Ideally CFG Clock should have been also enabled here. But
> + * it is done currently during board init routne
> + */
> + clk = clk_get_sys("pcie1", NULL);
> + if (IS_ERR(clk)) {
> + pr_err("%s:couldn't get clk for pcie1\n", __func__);
> + goto err_irq;
> + }
> + if (clk_enable(clk)) {
> + pr_err("%s:couldn't enable clk for pcie1\n", __func__);
> + goto err_irq;
> + }
> + } else if (pdev->id == 2) {
> + /*
> + * Ideally CFG Clock should have been also enabled here. But
> + * it is done currently during board init routne
> + */
> + clk = clk_get_sys("pcie2", NULL);
> + if (IS_ERR(clk)) {
> + pr_err("%s:couldn't get clk for pcie2\n", __func__);
> + goto err_irq;
> + }
> + if (clk_enable(clk)) {
> + pr_err("%s:couldn't enable clk for pcie2\n", __func__);
> + goto err_irq;
> + }
> + }
> + spear13xx_pcie_device_init(config);
> +
> + return 0;
> +err_irq:
> + free_irq(irq, NULL);
> +err_iounmap:
> + iounmap(config->va_dbi_base);
> +err_iounmap_app:
> + iounmap(config->va_app_base);
> +err_kzalloc:
> + kfree(config);
> +err_rel_res:
> + release_mem_region(res1->start, resource_size(res1));
> +err_rel_res0:
> + release_mem_region(res0->start, resource_size(res0));
> + return status;
> +}
> +
> +static int __devexit spear_pcie_gadget_remove(struct platform_device *pdev)
> +{
> + struct resource *res0, *res1;
> + static struct pcie_gadget_target *target;
> + struct spear_pcie_gadget_config *config;
> + int irq;
> +
> + res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + irq = platform_get_irq(pdev, 0);
> + target = dev_get_drvdata(&pdev->dev);
> + config = &target->config;
> +
> + free_irq(irq, NULL);
> + iounmap(config->va_dbi_base);
> + iounmap(config->va_app_base);
> + release_mem_region(res1->start, resource_size(res1));
> + release_mem_region(res0->start, resource_size(res0));
> + configfs_unregister_subsystem(&target->subsys);
> + kfree(target);
> +
> + return 0;
> +}
> +
> +static void spear_pcie_gadget_shutdown(struct platform_device *pdev)
> +{
> +}
> +
> +static struct platform_driver spear_pcie_gadget_driver = {
> + .probe = spear_pcie_gadget_probe,
> + .remove = spear_pcie_gadget_remove,
> + .shutdown = spear_pcie_gadget_shutdown,
> + .driver = {
> + .name = "pcie-gadget-spear",
> + .bus = &platform_bus_type
> + },
> +};
> +
> +static int __init spear_pcie_gadget_init(void)
> +{
> + return platform_driver_register(&spear_pcie_gadget_driver);
> +}
> +module_init(spear_pcie_gadget_init);
> +
> +static void __exit spear_pcie_gadget_exit(void)
> +{
> + platform_driver_unregister(&spear_pcie_gadget_driver);
> +}
> +module_exit(spear_pcie_gadget_exit);
> +
> +MODULE_ALIAS("pcie-gadget-spear");
> +MODULE_AUTHOR("Pratyush Anand");
> +MODULE_LICENSE("GPL");
> --
> 1.6.0.2
>
> .
Signed-off-by: Pat Kane <peka...@gmail.com>
---
net/unix/af_unix.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 1663e1a..3873523 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1642,6 +1642,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
max_level = err + 1;
fds_sent = true;
+ unix_get_secdata(siocb->scm, skb);
+
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
if (err) {
kfree_skb(skb);
@@ -1930,6 +1932,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
} else {
/* Copy credentials */
scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
+ unix_set_secdata(siocb->scm, skb);
check_creds = 1;
}
--
1.7.1
> The unix_dgram routines add secdata to socket message,
> but the unix_stream routines do not. We add the two
> missing lines of code.
Please copy net...@vger.kernel.org on networking patches.
We who? :)
> Signed-off-by: Pat Kane <peka...@gmail.com>
> ---
> net/unix/af_unix.c | 3 +++
> 1 files changed, 3 insertions(+), 0 deletions(-)
>
> diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
> index 1663e1a..3873523 100644
> --- a/net/unix/af_unix.c
> +++ b/net/unix/af_unix.c
> @@ -1642,6 +1642,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
> max_level = err + 1;
> fds_sent = true;
>
> + unix_get_secdata(siocb->scm, skb);
> +
> err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
> if (err) {
> kfree_skb(skb);
> @@ -1930,6 +1932,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
> } else {
> /* Copy credentials */
> scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
> + unix_set_secdata(siocb->scm, skb);
> check_creds = 1;
> }
>
> --
gmail (?) mucked up the indentation tabs -- they are all spaces. Not good.
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
This patch enables this SPI driver works with DMA mode.
Signed-off-by: Tomoya MORINAGA <tomoya...@dsn.okisemi.com>
---
drivers/spi/spi_topcliff_pch.c | 612 ++++++++++++++++++++++++++++++++--------
1 files changed, 492 insertions(+), 120 deletions(-)
diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
index 88bd472..f543ff6 100644
--- a/drivers/spi/spi_topcliff_pch.c
+++ b/drivers/spi/spi_topcliff_pch.c
@@ -28,6 +28,9 @@
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/pch_dma.h>
+
/* Register offsets */
#define PCH_SPCR 0x00 /* SPI control register */
#define PCH_SPBRR 0x04 /* SPI baud rate register */
@@ -36,7 +39,7 @@
#define PCH_SPDRR 0x10 /* SPI read data register */
#define PCH_SSNXCR 0x18 /* SSN Expand Control Register */
#define PCH_SRST 0x1C /* SPI reset register */
-#define PCH_SPI_ADDRESS_SIZE 0x20
+#define PCH_ADDRESS_SIZE 0x20
#define PCH_SPSR_TFD 0x000007C0
#define PCH_SPSR_RFD 0x0000F800
@@ -54,8 +57,6 @@
#define STATUS_EXITING 2
#define PCH_SLEEP_TIME 10
-#define PCH_ADDRESS_SIZE 0x20
-
#define SSN_LOW 0x02U
#define SSN_NO_CONTROL 0x00U
#define PCH_MAX_CS 0xFF
@@ -75,6 +76,7 @@
#define SPSR_TFI_BIT (1 << 0)
#define SPSR_RFI_BIT (1 << 1)
#define SPSR_FI_BIT (1 << 2)
+#define SPSR_ORF_BIT (1 << 3)
#define SPBRR_SIZE_BIT (1 << 10)
#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
@@ -83,10 +85,9 @@
#define SPCR_RFIC_FIELD 20
#define SPCR_TFIC_FIELD 16
-#define SPSR_INT_BITS 0x1F
-#define MASK_SPBRR_SPBR_BITS (~((1 << 10) - 1))
-#define MASK_RFIC_SPCR_BITS (~(0xf << 20))
-#define MASK_TFIC_SPCR_BITS (~(0xf000f << 12))
+#define MASK_SPBRR_SPBR_BITS ((1 << 10) - 1)
+#define MASK_RFIC_SPCR_BITS (0xf << SPCR_RFIC_FIELD)
+#define MASK_TFIC_SPCR_BITS (0xf << SPCR_TFIC_FIELD)
#define PCH_CLOCK_HZ 50000000
#define PCH_MAX_SPBR 1023
@@ -102,6 +103,28 @@
*/
#define PCH_SPI_MAX_DEV 2
+#define PCH_BUF_SIZE 4096
+#define PCH_DMA_TRANS_SIZE 12
+
+static int use_dma = 1;
+
+struct pch_spi_dma_ctrl {
+ struct dma_async_tx_descriptor *desc_tx;
+ struct dma_async_tx_descriptor *desc_rx;
+ struct pch_dma_slave param_tx;
+ struct pch_dma_slave param_rx;
+ struct dma_chan *chan_tx;
+ struct dma_chan *chan_rx;
+ struct scatterlist *sg_tx_p;
+ struct scatterlist *sg_rx_p;
+ struct scatterlist sg_tx;
+ struct scatterlist sg_rx;
+ int nent;
+ void *tx_buf_virt;
+ void *rx_buf_virt;
+ dma_addr_t tx_buf_dma;
+ dma_addr_t rx_buf_dma;
+};
/**
* struct pch_spi_data - Holds the SPI channel specific details
* @io_remap_addr: The remapped PCI base address
@@ -140,6 +163,7 @@
*/
struct pch_spi_data {
void __iomem *io_remap_addr;
+ unsigned long io_base_addr;
struct spi_master *master;
struct work_struct work;
struct workqueue_struct *wk;
@@ -162,6 +186,8 @@ struct pch_spi_data {
struct pch_spi_board_data *board_dat;
struct platform_device *plat_dev;
int ch;
+ struct pch_spi_dma_ctrl dma;
+ int use_dma;
u8 irq_reg_sts;
};
@@ -273,10 +299,10 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */
/* reset rx threshold */
- reg_spcr_val &= MASK_RFIC_SPCR_BITS;
+ reg_spcr_val &= ~MASK_RFIC_SPCR_BITS;
reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD);
- iowrite32(((reg_spcr_val) &= (~(SPCR_RFIE_BIT))),
- (io_remap_addr + PCH_SPCR));
+
+ iowrite32(reg_spcr_val, (io_remap_addr + PCH_SPCR));
}
/* update counts */
@@ -287,12 +313,15 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
/* if transfer complete interrupt */
if (reg_spsr_val & SPSR_FI_BIT) {
- /* disable FI & RFI interrupts */
- pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
- SPCR_FIE_BIT | SPCR_RFIE_BIT);
+ if (tx_index < bpw_len)
+ dev_err(&data->master->dev,
+ "%s : Transfer is not completed", __func__);
+ /* disable interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
/* transfer is completed;inform pch_spi_process_messages */
data->transfer_complete = true;
+ data->transfer_active = false;
wake_up(&data->wait);
}
}
@@ -316,11 +345,16 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
"%s returning due to suspend\n", __func__);
return IRQ_NONE;
}
+ if (data->use_dma)
+ return IRQ_NONE;
io_remap_addr = data->io_remap_addr;
spsr = io_remap_addr + PCH_SPSR;
reg_spsr_val = ioread32(spsr);
+ if (reg_spsr_val & SPSR_ORF_BIT)
+ dev_err(&board_dat->pdev->dev, "%s Over run error", __func__);
+
/* Check if the interrupt is for SPI device */
if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) {
pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr);
@@ -346,7 +380,7 @@ static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz)
if (n_spbr > PCH_MAX_SPBR)
n_spbr = PCH_MAX_SPBR;
- pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS);
+ pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, MASK_SPBRR_SPBR_BITS);
}
/**
@@ -454,26 +488,27 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
dev_dbg(&pspi->dev, "%s Transfer List not empty. "
"Transfer Speed is set.\n", __func__);
+ spin_lock_irqsave(&data->lock, flags);
/* validate Tx/Rx buffers and Transfer length */
list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
if (!transfer->tx_buf && !transfer->rx_buf) {
dev_err(&pspi->dev,
"%s Tx and Rx buffer NULL\n", __func__);
retval = -EINVAL;
- goto err_out;
+ goto err_return_spinlock;
}
if (!transfer->len) {
dev_err(&pspi->dev, "%s Transfer length invalid\n",
__func__);
retval = -EINVAL;
- goto err_out;
+ goto err_return_spinlock;
}
dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length"
" valid\n", __func__);
- /* if baud rate hs been specified validate the same */
+ /* if baud rate has been specified validate the same */
if (transfer->speed_hz > PCH_MAX_BAUDRATE)
transfer->speed_hz = PCH_MAX_BAUDRATE;
@@ -484,25 +519,24 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
retval = -EINVAL;
dev_err(&pspi->dev,
"%s Invalid bits per word\n", __func__);
- goto err_out;
+ goto err_return_spinlock;
}
}
}
-
- spin_lock_irqsave(&data->lock, flags);
+ spin_unlock_irqrestore(&data->lock, flags);
/* We won't process any messages if we have been asked to terminate */
if (data->status == STATUS_EXITING) {
dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__);
retval = -ESHUTDOWN;
- goto err_return_spinlock;
+ goto err_out;
}
/* If suspended ,return -EINVAL */
if (data->board_dat->suspend_sts) {
dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__);
retval = -EINVAL;
- goto err_return_spinlock;
+ goto err_out;
}
/* set status of message */
@@ -510,9 +544,11 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status);
pmsg->status = -EINPROGRESS;
-
+ spin_lock_irqsave(&data->lock, flags);
/* add message to queue */
list_add_tail(&pmsg->queue, &data->queue);
+ spin_unlock_irqrestore(&data->lock, flags);
+
dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__);
/* schedule work queue to run */
@@ -521,11 +557,13 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
retval = 0;
-err_return_spinlock:
- spin_unlock_irqrestore(&data->lock, flags);
err_out:
dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
return retval;
+err_return_spinlock:
+ dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return retval;
}
static inline void pch_spi_select_chip(struct pch_spi_data *data,
@@ -546,8 +584,7 @@ static inline void pch_spi_select_chip(struct pch_spi_data *data,
pch_spi_setup_transfer(pspi);
}
-static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
- struct spi_message **ppmsg)
+static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw)
{
int size;
u32 n_writes;
@@ -556,8 +593,6 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
const u8 *tx_buf;
const u16 *tx_sbuf;
- pmsg = *ppmsg;
-
/* set baud rate if needed */
if (data->cur_trans->speed_hz) {
dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
@@ -566,8 +601,8 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
/* set bits per word if needed */
if (data->cur_trans->bits_per_word &&
- (data->current_msg->spi->bits_per_word !=\
- data->cur_trans->bits_per_word)) {
+ (data->current_msg->spi->bits_per_word !=\
+ data->cur_trans->bits_per_word)) {
dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__);
pch_spi_set_bits_per_word(data->master,
data->cur_trans->bits_per_word);
@@ -641,9 +676,9 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
data->transfer_active = true;
}
-static void pch_spi_nomore_transfer(struct pch_spi_data *data,
- struct spi_message *pmsg)
+static void pch_spi_nomore_transfer(struct pch_spi_data *data)
{
+ struct spi_message *pmsg;
dev_dbg(&data->master->dev, "%s called\n", __func__);
/* Invoke complete callback
* [To the spi core..indicating end of transfer] */
@@ -694,30 +729,21 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data,
static void pch_spi_set_ir(struct pch_spi_data *data)
{
- /* enable interrupts */
- if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) {
+ /* enable interrupts, set threshold, enable SPI */
+ if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH)
/* set receive threshold to PCH_RX_THOLD */
pch_spi_setclr_reg(data->master, PCH_SPCR,
- PCH_RX_THOLD << SPCR_RFIC_FIELD,
- ~MASK_RFIC_SPCR_BITS);
- /* enable FI and RFI interrupts */
- pch_spi_setclr_reg(data->master, PCH_SPCR,
- SPCR_RFIE_BIT | SPCR_FIE_BIT, 0);
- } else {
+ PCH_RX_THOLD << SPCR_RFIC_FIELD |
+ SPCR_FIE_BIT | SPCR_RFIE_BIT |
+ SPCR_ORIE_BIT | SPCR_SPE_BIT,
+ MASK_RFIC_SPCR_BITS | PCH_ALL);
+ else
/* set receive threshold to maximum */
pch_spi_setclr_reg(data->master, PCH_SPCR,
- PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD,
- ~MASK_TFIC_SPCR_BITS);
- /* enable FI interrupt */
- pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_FIE_BIT, 0);
- }
-
- dev_dbg(&data->master->dev,
- "%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
-
- /* SPI set enable */
- pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT,
- 0);
+ PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD |
+ SPCR_FIE_BIT | SPCR_ORIE_BIT |
+ SPCR_SPE_BIT,
+ MASK_RFIC_SPCR_BITS | PCH_ALL);
/* Wait until the transfer completes; go to sleep after
initiating the transfer. */
@@ -730,15 +756,13 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
dev_dbg(&data->master->dev,
"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
- data->transfer_active = false;
- dev_dbg(&data->master->dev,
- "%s set data->transfer_active = false\n", __func__);
-
/* clear all interrupts */
pch_spi_writereg(data->master, PCH_SPSR,
pch_spi_readreg(data->master, PCH_SPSR));
- /* disable interrupts */
- pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+ /* Disable interrupts and SPI transfer */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL | SPCR_SPE_BIT);
+ /* clear FIFO */
+ pch_spi_clear_fifo(data->master);
}
static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
@@ -762,6 +786,328 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
}
}
+static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw)
+{
+ int j;
+ u8 *rx_buf;
+ u16 *rx_sbuf;
+ const u8 *rx_dma_buf;
+ const u16 *rx_dma_sbuf;
+
+ /* copy Rx Data */
+ if (!data->cur_trans->rx_buf)
+ return;
+
+ if (bpw == 8) {
+ rx_buf = data->cur_trans->rx_buf;
+ rx_dma_buf = data->dma.rx_buf_virt;
+ for (j = 0; j < data->bpw_len; j++)
+ *rx_buf++ = *rx_dma_buf++ & 0xFF;
+ } else {
+ rx_sbuf = data->cur_trans->rx_buf;
+ rx_dma_sbuf = data->dma.rx_buf_virt;
+ for (j = 0; j < data->bpw_len; j++)
+ *rx_sbuf++ = *rx_dma_sbuf++;
+ }
+}
+
+static void pch_spi_start_transfer(struct pch_spi_data *data)
+{
+ struct pch_spi_dma_ctrl *dma;
+ unsigned long flags;
+
+ dma = &data->dma;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* disable interrupts, SPI set enable */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_SPE_BIT, PCH_ALL);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* Wait until the transfer completes; go to sleep after
+ initiating the transfer. */
+ dev_dbg(&data->master->dev,
+ "%s:waiting for transfer to get over\n", __func__);
+ wait_event_interruptible(data->wait, data->transfer_complete);
+
+ dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent,
+ DMA_FROM_DEVICE);
+ async_tx_ack(dma->desc_rx);
+ async_tx_ack(dma->desc_tx);
+ kfree(dma->sg_tx_p);
+ kfree(dma->sg_rx_p);
+
+ spin_lock_irqsave(&data->lock, flags);
+ pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
+ dev_dbg(&data->master->dev,
+ "%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
+
+ /* clear fifo threshold, disable interrupts, disable SPI transfer */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
+ MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS | PCH_ALL |
+ SPCR_SPE_BIT);
+ /* clear all interrupts */
+ pch_spi_writereg(data->master, PCH_SPSR,
+ pch_spi_readreg(data->master, PCH_SPSR));
+ /* clear FIFO */
+ pch_spi_clear_fifo(data->master);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void pch_dma_rx_complete(void *arg)
+{
+ struct pch_spi_data *data = arg;
+
+ /* transfer is completed;inform pch_spi_process_messages_dma */
+ data->transfer_complete = true;
+ wake_up_interruptible(&data->wait);
+}
+
+static bool pch_spi_filter(struct dma_chan *chan, void *slave)
+{
+ struct pch_dma_slave *param = slave;
+
+ if ((chan->chan_id == param->chan_id) &&
+ (param->dma_dev == chan->device->dev)) {
+ chan->private = param;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+ struct pci_dev *dma_dev;
+ struct pch_dma_slave *param;
+ struct pch_spi_dma_ctrl *dma;
+ unsigned int width;
+
+ if (bpw == 8)
+ width = PCH_DMA_WIDTH_1_BYTE;
+ else
+ width = PCH_DMA_WIDTH_2_BYTES;
+
+ dma = &data->dma;
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* Get DMA's dev information */
+ dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(12, 0));
+
+ /* Set Tx DMA */
+ param = &dma->param_tx;
+ param->dma_dev = &dma_dev->dev;
+ param->chan_id = data->master->bus_num * 2; /* Tx = 0, 2 */
+ param->tx_reg = data->io_base_addr + PCH_SPDWR;
+ param->width = width;
+ chan = dma_request_channel(mask, pch_spi_filter, param);
+ if (!chan) {
+ dev_err(&data->master->dev,
+ "ERROR: dma_request_channel FAILS(Tx)\n");
+ data->use_dma = 0;
+ return;
+ }
+ dma->chan_tx = chan;
+
+ /* Set Rx DMA */
+ param = &dma->param_rx;
+ param->dma_dev = &dma_dev->dev;
+ param->chan_id = data->master->bus_num * 2 + 1; /* Rx = Tx + 1 */
+ param->rx_reg = data->io_base_addr + PCH_SPDRR;
+ param->width = width;
+ chan = dma_request_channel(mask, pch_spi_filter, param);
+ if (!chan) {
+ dev_err(&data->master->dev,
+ "ERROR: dma_request_channel FAILS(Rx)\n");
+ dma_release_channel(dma->chan_tx);
+ dma->chan_tx = NULL;
+ data->use_dma = 0;
+ return;
+ }
+ dma->chan_rx = chan;
+}
+
+static void pch_spi_release_dma(struct pch_spi_data *data)
+{
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+ if (dma->chan_tx) {
+ dma_release_channel(dma->chan_tx);
+ dma->chan_tx = NULL;
+ }
+ if (dma->chan_rx) {
+ dma_release_channel(dma->chan_rx);
+ dma->chan_rx = NULL;
+ }
+ return;
+}
+
+static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
+{
+ const u8 *tx_buf;
+ const u16 *tx_sbuf;
+ u8 *tx_dma_buf;
+ u16 *tx_dma_sbuf;
+ struct scatterlist *sg;
+ struct dma_async_tx_descriptor *desc_tx;
+ struct dma_async_tx_descriptor *desc_rx;
+ int num;
+ int i;
+ int size;
+ int rem;
+ unsigned long flags;
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+
+ /* set baud rate if needed */
+ if (data->cur_trans->speed_hz) {
+ dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
+ spin_lock_irqsave(&data->lock, flags);
+ pch_spi_set_baud_rate(data->master, data->cur_trans->speed_hz);
+ spin_unlock_irqrestore(&data->lock, flags);
+ }
+
+ /* set bits per word if needed */
+ if (data->cur_trans->bits_per_word &&
+ (data->current_msg->spi->bits_per_word !=
+ data->cur_trans->bits_per_word)) {
+ dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__);
+ spin_lock_irqsave(&data->lock, flags);
+ pch_spi_set_bits_per_word(data->master,
+ data->cur_trans->bits_per_word);
+ spin_unlock_irqrestore(&data->lock, flags);
+ *bpw = data->cur_trans->bits_per_word;
+ } else {
+ *bpw = data->current_msg->spi->bits_per_word;
+ }
+ data->bpw_len = data->cur_trans->len / (*bpw / 8);
+
+ /* copy Tx Data */
+ if (data->cur_trans->tx_buf != NULL) {
+ if (*bpw == 8) {
+ tx_buf = data->cur_trans->tx_buf;
+ tx_dma_buf = dma->tx_buf_virt;
+ for (i = 0; i < data->bpw_len; i++)
+ *tx_dma_buf++ = *tx_buf++;
+ } else {
+ tx_sbuf = data->cur_trans->tx_buf;
+ tx_dma_sbuf = dma->tx_buf_virt;
+ for (i = 0; i < data->bpw_len; i++)
+ *tx_dma_sbuf++ = *tx_sbuf++;
+ }
+ }
+ if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
+ num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
+ size = PCH_DMA_TRANS_SIZE;
+ rem = data->bpw_len % PCH_DMA_TRANS_SIZE;
+ } else {
+ num = 1;
+ size = data->bpw_len;
+ rem = data->bpw_len;
+ }
+ dev_dbg(&data->master->dev, "%s num=%d size=%d rem=%d\n",
+ __func__, num, size, rem);
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* set receive fifo threshold and transmit fifo threshold */
+ pch_spi_setclr_reg(data->master, PCH_SPCR,
+ ((size - 1) << SPCR_RFIC_FIELD) |
+ ((PCH_MAX_FIFO_DEPTH - PCH_DMA_TRANS_SIZE) <<
+ SPCR_TFIC_FIELD),
+ MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* RX */
+ dma->sg_rx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+ sg_init_table(dma->sg_rx_p, num); /* Initialize SG table */
+ /* offset, length setting */
+ sg = dma->sg_rx_p;
+ for (i = 0; i < num; i++, sg++) {
+ if (i == 0) {
+ sg->offset = 0;
+ sg_set_page(sg, virt_to_page(dma->rx_buf_virt), rem,
+ sg->offset);
+ sg_dma_len(sg) = rem;
+ } else {
+ sg->offset = rem + size * (i - 1);
+ sg->offset = sg->offset * (*bpw / 8);
+ sg_set_page(sg, virt_to_page(dma->rx_buf_virt), size,
+ sg->offset);
+ sg_dma_len(sg) = size;
+ }
+ sg_dma_address(sg) = dma->rx_buf_dma + sg->offset;
+ }
+ sg = dma->sg_rx_p;
+ desc_rx = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx, sg,
+ num, DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_rx) {
+ dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n",
+ __func__);
+ return;
+ }
+ dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_FROM_DEVICE);
+ desc_rx->callback = pch_dma_rx_complete;
+ desc_rx->callback_param = data;
+ dma->nent = num;
+ dma->desc_rx = desc_rx;
+
+ /* TX */
+ dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+ sg_init_table(dma->sg_tx_p, num); /* Initialize SG table */
+ /* offset, length setting */
+ sg = dma->sg_tx_p;
+ for (i = 0; i < num; i++, sg++) {
+ if (i == 0) {
+ sg->offset = 0;
+ sg_set_page(sg, virt_to_page(dma->tx_buf_virt), rem,
+ sg->offset);
+ sg_dma_len(sg) = rem;
+ } else {
+ sg->offset = rem + size * (i - 1);
+ sg->offset = sg->offset * (*bpw / 8);
+ sg_set_page(sg, virt_to_page(dma->tx_buf_virt), size,
+ sg->offset);
+ sg_dma_len(sg) = size;
+ }
+ sg_dma_address(sg) = dma->tx_buf_dma + sg->offset;
+ }
+ sg = dma->sg_tx_p;
+ desc_tx = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx,
+ sg, num, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_tx) {
+ dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n",
+ __func__);
+ return;
+ }
+ dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_TO_DEVICE);
+ desc_tx->callback = NULL;
+ desc_tx->callback_param = data;
+ dma->nent = num;
+ dma->desc_tx = desc_tx;
+
+ dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing "
+ "0x2 to SSNXCR\n", __func__);
+
+ spin_lock_irqsave(&data->lock, flags);
+ pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW);
+ desc_rx->tx_submit(desc_rx);
+ desc_tx->tx_submit(desc_tx);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* reset transfer complete flag */
+ data->transfer_complete = false;
+}
+
static void pch_spi_process_messages(struct work_struct *pwork)
{
struct spi_message *pmsg;
@@ -772,30 +1118,23 @@ static void pch_spi_process_messages(struct work_struct *pwork)
dev_dbg(&data->master->dev, "%s data initialized\n", __func__);
spin_lock(&data->lock);
-
/* check if suspend has been initiated;if yes flush queue */
if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) {
- dev_dbg(&data->master->dev,
- "%s suspend/remove initiated,flushing queue\n",
- __func__);
-
+ dev_dbg(&data->master->dev, "%s suspend/remove initiated,"
+ "flushing queue\n", __func__);
list_for_each_entry(pmsg, data->queue.next, queue) {
pmsg->status = -EIO;
-
if (pmsg->complete != 0) {
spin_unlock(&data->lock);
pmsg->complete(pmsg->context);
spin_lock(&data->lock);
}
-
/* delete from queue */
list_del_init(&pmsg->queue);
}
-
spin_unlock(&data->lock);
return;
}
-
data->bcurrent_msg_processing = true;
dev_dbg(&data->master->dev,
"%s Set data->bcurrent_msg_processing= true\n", __func__);
@@ -803,62 +1142,47 @@ static void pch_spi_process_messages(struct work_struct *pwork)
/* Get the message from the queue and delete it from there. */
data->current_msg = list_entry(data->queue.next, struct spi_message,
queue);
-
list_del_init(&data->current_msg->queue);
-
data->current_msg->status = 0;
-
pch_spi_select_chip(data, data->current_msg->spi);
-
spin_unlock(&data->lock);
+ if (data->use_dma)
+ pch_spi_request_dma(data,
+ data->current_msg->spi->bits_per_word);
do {
/* If we are already processing a message get the next
transfer structure from the message otherwise retrieve
the 1st transfer request from the message. */
spin_lock(&data->lock);
-
if (data->cur_trans == NULL) {
data->cur_trans =
- list_entry(data->current_msg->transfers.
- next, struct spi_transfer,
- transfer_list);
- dev_dbg(&data->master->dev,
- "%s :Getting 1st transfer message\n", __func__);
+ list_entry(data->current_msg->transfers.next,
+ struct spi_transfer, transfer_list);
+ dev_dbg(&data->master->dev, "%s "
+ ":Getting 1st transfer message\n", __func__);
} else {
data->cur_trans =
- list_entry(data->cur_trans->transfer_list.next,
- struct spi_transfer,
- transfer_list);
- dev_dbg(&data->master->dev,
- "%s :Getting next transfer message\n",
- __func__);
+ list_entry(data->cur_trans->transfer_list.next,
+ struct spi_transfer, transfer_list);
+ dev_dbg(&data->master->dev, "%s "
+ ":Getting next transfer message\n", __func__);
}
-
spin_unlock(&data->lock);
- pch_spi_set_tx(data, &bpw, &pmsg);
-
- /* Control interrupt*/
- pch_spi_set_ir(data);
-
- /* Disable SPI transfer */
- pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, 0,
- SPCR_SPE_BIT);
-
- /* clear FIFO */
- pch_spi_clear_fifo(data->master);
-
- /* copy Rx Data */
- pch_spi_copy_rx_data(data, bpw);
-
- /* free memory */
- kfree(data->pkt_rx_buff);
- data->pkt_rx_buff = NULL;
-
- kfree(data->pkt_tx_buff);
- data->pkt_tx_buff = NULL;
-
+ if (data->use_dma) {
+ pch_spi_handle_dma(data, &bpw);
+ pch_spi_start_transfer(data);
+ pch_spi_copy_rx_data_for_dma(data, bpw);
+ } else {
+ pch_spi_set_tx(data, &bpw);
+ pch_spi_set_ir(data);
+ pch_spi_copy_rx_data(data, bpw);
+ kfree(data->pkt_rx_buff);
+ data->pkt_rx_buff = NULL;
+ kfree(data->pkt_tx_buff);
+ data->pkt_tx_buff = NULL;
+ }
/* increment message count */
data->current_msg->actual_length += data->cur_trans->len;
@@ -875,16 +1199,17 @@ static void pch_spi_process_messages(struct work_struct *pwork)
}
spin_lock(&data->lock);
-
/* No more transfer in this message. */
if ((data->cur_trans->transfer_list.next) ==
&(data->current_msg->transfers)) {
- pch_spi_nomore_transfer(data, pmsg);
+ pch_spi_nomore_transfer(data);
}
-
spin_unlock(&data->lock);
} while (data->cur_trans != NULL);
+
+ if (data->use_dma)
+ pch_spi_release_dma(data);
}
static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
@@ -911,6 +1236,7 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
/* create workqueue */
data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+
if (!data->wk) {
dev_err(&board_dat->pdev->dev,
"%s create_singlet hread_workqueue failed\n", __func__);
@@ -937,6 +1263,35 @@ err_return:
return retval;
}
+static void pch_free_dma_buf(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
+{
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+ if (dma->tx_buf_dma)
+ dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+ dma->tx_buf_virt, dma->tx_buf_dma);
+ if (dma->rx_buf_dma)
+ dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+ dma->rx_buf_virt, dma->rx_buf_dma);
+ return;
+}
+
+static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
+{
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+ /* Get Consistent memory for Tx DMA */
+ dma->tx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+ PCH_BUF_SIZE, &dma->tx_buf_dma, GFP_KERNEL);
+ /* Get Consistent memory for Rx DMA */
+ dma->rx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+ PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL);
+}
+
static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
{
int ret;
@@ -944,6 +1299,8 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
struct pch_spi_data *data;
+ dev_dbg(&plat_dev->dev, "%s:debug\n", __func__);
+
master = spi_alloc_master(&board_dat->pdev->dev,
sizeof(struct pch_spi_data));
if (!master) {
@@ -957,9 +1314,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
platform_set_drvdata(plat_dev, data);
- /* baseaddress + 0x20(offset) */
+ /* baseaddress + address offset) */
+ data->io_base_addr = pci_resource_start(board_dat->pdev, 1) +
+ PCH_ADDRESS_SIZE * plat_dev->id;
data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
- 0x20 * plat_dev->id;
+ PCH_ADDRESS_SIZE * plat_dev->id;
if (!data->io_remap_addr) {
dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
ret = -ENOMEM;
@@ -980,6 +1339,7 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
data->n_curnt_chip = 255;
data->status = STATUS_RUNNING;
data->ch = plat_dev->id;
+ data->use_dma = use_dma;
INIT_LIST_HEAD(&data->queue);
spin_lock_init(&data->lock);
@@ -1010,6 +1370,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
goto err_spi_register_master;
}
+ if (use_dma) {
+ dev_info(&plat_dev->dev, "Use DMA for data transfers\n");
+ pch_alloc_dma_buf(board_dat, data);
+ }
+
return 0;
err_spi_register_master:
@@ -1029,22 +1394,27 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
struct pch_spi_data *data = platform_get_drvdata(plat_dev);
int count;
+ unsigned long flags;
dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
__func__, plat_dev->id, board_dat->pdev->irq);
+
+ if (use_dma)
+ pch_free_dma_buf(board_dat, data);
+
/* check for any pending messages; no action is taken if the queue
* is still full; but at least we tried. Unload anyway */
count = 500;
- spin_lock(&data->lock);
+ spin_lock_irqsave(&data->lock, flags);
data->status = STATUS_EXITING;
while ((list_empty(&data->queue) == 0) && --count) {
dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
__func__);
- spin_unlock(&data->lock);
+ spin_unlock_irqrestore(&data->lock, flags);
msleep(PCH_SLEEP_TIME);
- spin_lock(&data->lock);
+ spin_lock_irqsave(&data->lock, flags);
}
- spin_unlock(&data->lock);
+ spin_unlock_irqrestore(&data->lock, flags);
pch_spi_free_resources(board_dat, data);
/* disable interrupts & free IRQ */
@@ -1081,8 +1451,8 @@ static int pch_spi_pd_suspend(struct platform_device *pd_dev,
/* check if the current message is processed:
Only after thats done the transfer will be suspended */
count = 255;
- while ((--count) > 0)
- if (!(data->bcurrent_msg_processing)) {
+ while ((--count) > 0) {
+ if (!(data->bcurrent_msg_processing))
break;
msleep(PCH_SLEEP_TIME);
}
@@ -1123,7 +1493,6 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
"%s request_irq failed\n", __func__);
return retval;
}
-
/* reset PCH SPI h/w */
pch_spi_reset(data->master);
pch_spi_set_master_mode(data->master);
@@ -1213,7 +1582,6 @@ static int __devinit pch_spi_probe(struct pci_dev *pdev,
}
pci_set_drvdata(pdev, pd_dev_save);
-
return 0;
err_platform_device:
@@ -1326,5 +1694,9 @@ static void __exit pch_spi_exit(void)
}
module_exit(pch_spi_exit);
+module_param(use_dma, int, 0644);
+MODULE_PARM_DESC(use_dma,
+ "to use DMA for data transfers pass 1 else 0; default 1");
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI Driver");
--
1.7.4
***Modify Grant's comments.
- Delete unrelated whitespace
- Prevent device driver from accessing platform data
- Add __devinit and __devexit
- Save pdev->dev to pd_dev->dev.parent
- Have own suspend/resume processing in platform_driver.
- Care returned value in pch_spi_init
- Change unregister order
Support ML7213 device of OKI SEMICONDUCTOR.
ML7213 is companion chip of Intel Atom E6xx series for IVI(In-Vehicle Infotainment).
ML7213 is compatible for Intel EG20T PCH.
Signed-off-by: Tomoya MORINAGA <tomoya...@dsn.okisemi.com>
---
drivers/spi/Kconfig | 5 +-
drivers/spi/spi_topcliff_pch.c | 599 +++++++++++++++++++++-------------------
2 files changed, 317 insertions(+), 287 deletions(-)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index fc14b8d..ed6134b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -380,12 +380,15 @@ config SPI_TI_SSP
module will be called ti-ssp-spi.
config SPI_TOPCLIFF_PCH
- tristate "Topcliff PCH SPI Controller"
+ tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI controller"
depends on PCI
help
SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus
used in some x86 embedded processors.
+ This driver also supports the ML7213, a companion chip for the
+ Atom E6xx series and compatible with the Intel EG20T PCH.
+
config SPI_TXX9
tristate "Toshiba TXx9 SPI controller"
depends on GENERIC_GPIO && CPU_TX49XX
diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
index 79e48d4..88bd472 100644
--- a/drivers/spi/spi_topcliff_pch.c
+++ b/drivers/spi/spi_topcliff_pch.c
@@ -26,6 +26,7 @@
#include <linux/spi/spidev.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/platform_device.h>
/* Register offsets */
#define PCH_SPCR 0x00 /* SPI control register */
@@ -35,6 +36,7 @@
#define PCH_SPDRR 0x10 /* SPI read data register */
#define PCH_SSNXCR 0x18 /* SSN Expand Control Register */
#define PCH_SRST 0x1C /* SPI reset register */
+#define PCH_SPI_ADDRESS_SIZE 0x20
#define PCH_SPSR_TFD 0x000007C0
#define PCH_SPSR_RFD 0x0000F800
@@ -75,7 +77,8 @@
#define SPSR_FI_BIT (1 << 2)
#define SPBRR_SIZE_BIT (1 << 10)
-#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|SPCR_ORIE_BIT|SPCR_MDFIE_BIT)
+#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
+ SPCR_ORIE_BIT|SPCR_MDFIE_BIT)
#define SPCR_RFIC_FIELD 20
#define SPCR_TFIC_FIELD 16
@@ -88,6 +91,16 @@
#define PCH_CLOCK_HZ 50000000
#define PCH_MAX_SPBR 1023
+/* Definition for ML7213 by OKI SEMICONDUCTOR */
+#define PCI_VENDOR_ID_ROHM 0x10DB
+#define PCI_DEVICE_ID_ML7213_SPI 0x802c
+
+/*
+ * Set the number of SPI instance max
+ * Intel EG20T PCH : 1ch
+ * OKI SEMICONDUCTOR ML7213 IOH : 2ch
+*/
+#define PCH_SPI_MAX_DEV 2
/**
* struct pch_spi_data - Holds the SPI channel specific details
@@ -121,6 +134,9 @@
* @cur_trans: The current transfer that this SPI driver is
* handling
* @board_dat: Reference to the SPI device data structure
+ * @plat_dev: platform_device structure
+ * @ch: SPI channel number
+ * @irq_reg_sts: Status of IRQ registration
*/
struct pch_spi_data {
void __iomem *io_remap_addr;
@@ -144,27 +160,33 @@ struct pch_spi_data {
struct spi_message *current_msg;
struct spi_transfer *cur_trans;
struct pch_spi_board_data *board_dat;
+ struct platform_device *plat_dev;
+ int ch;
+ u8 irq_reg_sts;
};
/**
* struct pch_spi_board_data - Holds the SPI device specific details
* @pdev: Pointer to the PCI device
- * @irq_reg_sts: Status of IRQ registration
- * @pci_req_sts: Status of pci_request_regions
* @suspend_sts: Status of suspend
- * @data: Pointer to SPI channel data structure
+ * @num: The number of SPI device instance
*/
struct pch_spi_board_data {
struct pci_dev *pdev;
- u8 irq_reg_sts;
- u8 pci_req_sts;
u8 suspend_sts;
- struct pch_spi_data *data;
+ int num;
+};
+
+struct pch_pd_dev_save {
+ int num;
+ struct platform_device *pd_save[PCH_SPI_MAX_DEV];
+ struct pch_spi_board_data *board_dat;
};
static struct pci_device_id pch_spi_pcidev_id[] = {
- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_GE_SPI)},
- {0,}
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), 1, },
+ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, },
+ { }
};
/**
@@ -283,11 +305,11 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
static irqreturn_t pch_spi_handler(int irq, void *dev_id)
{
u32 reg_spsr_val;
- struct pch_spi_data *data;
void __iomem *spsr;
void __iomem *io_remap_addr;
irqreturn_t ret = IRQ_NONE;
- struct pch_spi_board_data *board_dat = dev_id;
+ struct pch_spi_data *data = dev_id;
+ struct pch_spi_board_data *board_dat = data->board_dat;
if (board_dat->suspend_sts) {
dev_dbg(&board_dat->pdev->dev,
@@ -295,10 +317,8 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
return IRQ_NONE;
}
- data = board_dat->data;
io_remap_addr = data->io_remap_addr;
spsr = io_remap_addr + PCH_SPSR;
-
reg_spsr_val = ioread32(spsr);
/* Check if the interrupt is for SPI device */
@@ -412,7 +432,6 @@ static int pch_spi_setup(struct spi_device *pspi)
static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
{
-
struct spi_transfer *transfer;
struct pch_spi_data *data = spi_master_get_devdata(pspi->master);
int retval;
@@ -547,7 +566,8 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
/* set bits per word if needed */
if (data->cur_trans->bits_per_word &&
- (data->current_msg->spi->bits_per_word != data->cur_trans->bits_per_word)) {
+ (data->current_msg->spi->bits_per_word !=\
+ data->cur_trans->bits_per_word)) {
dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__);
pch_spi_set_bits_per_word(data->master,
data->cur_trans->bits_per_word);
@@ -621,7 +641,6 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
data->transfer_active = true;
}
-
static void pch_spi_nomore_transfer(struct pch_spi_data *data,
struct spi_message *pmsg)
{
@@ -697,7 +716,8 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
"%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
/* SPI set enable */
- pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT, 0);
+ pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT,
+ 0);
/* Wait until the transfer completes; go to sleep after
initiating the transfer. */
@@ -742,7 +762,6 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
}
}
-
static void pch_spi_process_messages(struct work_struct *pwork)
{
struct spi_message *pmsg;
@@ -868,117 +887,49 @@ static void pch_spi_process_messages(struct work_struct *pwork)
} while (data->cur_trans != NULL);
}
-static void pch_spi_free_resources(struct pch_spi_board_data *board_dat)
+static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
{
dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
/* free workqueue */
- if (board_dat->data->wk != NULL) {
- destroy_workqueue(board_dat->data->wk);
- board_dat->data->wk = NULL;
+ if (data->wk != NULL) {
+ destroy_workqueue(data->wk);
+ data->wk = NULL;
dev_dbg(&board_dat->pdev->dev,
"%s destroy_workqueue invoked successfully\n",
__func__);
}
-
- /* disable interrupts & free IRQ */
- if (board_dat->irq_reg_sts) {
- /* disable interrupts */
- pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
- PCH_ALL);
-
- /* free IRQ */
- free_irq(board_dat->pdev->irq, board_dat);
-
- dev_dbg(&board_dat->pdev->dev,
- "%s free_irq invoked successfully\n", __func__);
-
- board_dat->irq_reg_sts = false;
- }
-
- /* unmap PCI base address */
- if (board_dat->data->io_remap_addr != 0) {
- pci_iounmap(board_dat->pdev, board_dat->data->io_remap_addr);
-
- board_dat->data->io_remap_addr = 0;
-
- dev_dbg(&board_dat->pdev->dev,
- "%s pci_iounmap invoked successfully\n", __func__);
- }
-
- /* release PCI region */
- if (board_dat->pci_req_sts) {
- pci_release_regions(board_dat->pdev);
- dev_dbg(&board_dat->pdev->dev,
- "%s pci_release_regions invoked successfully\n",
- __func__);
- board_dat->pci_req_sts = false;
- }
}
-static int pch_spi_get_resources(struct pch_spi_board_data *board_dat)
+static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
{
- void __iomem *io_remap_addr;
- int retval;
+ int retval = 0;
+
dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
/* create workqueue */
- board_dat->data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
- if (!board_dat->data->wk) {
+ data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+ if (!data->wk) {
dev_err(&board_dat->pdev->dev,
"%s create_singlet hread_workqueue failed\n", __func__);
retval = -EBUSY;
goto err_return;
}
- dev_dbg(&board_dat->pdev->dev,
- "%s create_singlethread_workqueue success\n", __func__);
-
- retval = pci_request_regions(board_dat->pdev, KBUILD_MODNAME);
- if (retval != 0) {
- dev_err(&board_dat->pdev->dev,
- "%s request_region failed\n", __func__);
- goto err_return;
- }
-
- board_dat->pci_req_sts = true;
-
- io_remap_addr = pci_iomap(board_dat->pdev, 1, 0);
- if (io_remap_addr == 0) {
- dev_err(&board_dat->pdev->dev,
- "%s pci_iomap failed\n", __func__);
- retval = -ENOMEM;
- goto err_return;
- }
-
- /* calculate base address for all channels */
- board_dat->data->io_remap_addr = io_remap_addr;
-
/* reset PCH SPI h/w */
- pch_spi_reset(board_dat->data->master);
+ pch_spi_reset(data->master);
dev_dbg(&board_dat->pdev->dev,
"%s pch_spi_reset invoked successfully\n", __func__);
- /* register IRQ */
- retval = request_irq(board_dat->pdev->irq, pch_spi_handler,
- IRQF_SHARED, KBUILD_MODNAME, board_dat);
- if (retval != 0) {
- dev_err(&board_dat->pdev->dev,
- "%s request_irq failed\n", __func__);
- goto err_return;
- }
-
- dev_dbg(&board_dat->pdev->dev, "%s request_irq returned=%d\n",
- __func__, retval);
-
- board_dat->irq_reg_sts = true;
dev_dbg(&board_dat->pdev->dev, "%s data->irq_reg_sts=true\n", __func__);
err_return:
if (retval != 0) {
dev_err(&board_dat->pdev->dev,
"%s FAIL:invoking pch_spi_free_resources\n", __func__);
- pch_spi_free_resources(board_dat);
+ pch_spi_free_resources(board_dat, data);
}
dev_dbg(&board_dat->pdev->dev, "%s Return=%d\n", __func__, retval);
@@ -986,255 +937,343 @@ err_return:
return retval;
}
-static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
{
-
+ int ret;
struct spi_master *master;
+ struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
+ struct pch_spi_data *data;
- struct pch_spi_board_data *board_dat;
- int retval;
-
- dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
-
- /* allocate memory for private data */
- board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
- if (board_dat == NULL) {
- dev_err(&pdev->dev,
- " %s memory allocation for private data failed\n",
- __func__);
- retval = -ENOMEM;
- goto err_kmalloc;
- }
-
- dev_dbg(&pdev->dev,
- "%s memory allocation for private data success\n", __func__);
-
- /* enable PCI device */
- retval = pci_enable_device(pdev);
- if (retval != 0) {
- dev_err(&pdev->dev, "%s pci_enable_device FAILED\n", __func__);
-
- goto err_pci_en_device;
+ master = spi_alloc_master(&board_dat->pdev->dev,
+ sizeof(struct pch_spi_data));
+ if (!master) {
+ dev_err(&plat_dev->dev, "spi_alloc_master[%d] failed.\n",
+ plat_dev->id);
+ return -ENOMEM;
}
- dev_dbg(&pdev->dev, "%s pci_enable_device returned=%d\n",
- __func__, retval);
+ data = spi_master_get_devdata(master);
+ data->master = master;
- board_dat->pdev = pdev;
+ platform_set_drvdata(plat_dev, data);
- /* alllocate memory for SPI master */
- master = spi_alloc_master(&pdev->dev, sizeof(struct pch_spi_data));
- if (master == NULL) {
- retval = -ENOMEM;
- dev_err(&pdev->dev, "%s Fail.\n", __func__);
- goto err_spi_alloc_master;
+ /* baseaddress + 0x20(offset) */
+ data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
+ 0x20 * plat_dev->id;
+ if (!data->io_remap_addr) {
+ dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
+ ret = -ENOMEM;
+ goto err_pci_iomap;
}
- dev_dbg(&pdev->dev,
- "%s spi_alloc_master returned non NULL\n", __func__);
+ dev_dbg(&plat_dev->dev, "[ch%d] remap_addr=%p\n",
+ plat_dev->id, data->io_remap_addr);
/* initialize members of SPI master */
- master->bus_num = -1;
+ master->bus_num = plat_dev->id;
master->num_chipselect = PCH_MAX_CS;
master->setup = pch_spi_setup;
master->transfer = pch_spi_transfer;
- dev_dbg(&pdev->dev,
- "%s transfer member of SPI master initialized\n", __func__);
- board_dat->data = spi_master_get_devdata(master);
+ data->board_dat = board_dat;
+ data->plat_dev = plat_dev;
+ data->n_curnt_chip = 255;
+ data->status = STATUS_RUNNING;
+ data->ch = plat_dev->id;
- board_dat->data->master = master;
- board_dat->data->n_curnt_chip = 255;
- board_dat->data->board_dat = board_dat;
- board_dat->data->status = STATUS_RUNNING;
+ INIT_LIST_HEAD(&data->queue);
+ spin_lock_init(&data->lock);
+ INIT_WORK(&data->work, pch_spi_process_messages);
+ init_waitqueue_head(&data->wait);
- INIT_LIST_HEAD(&board_dat->data->queue);
- spin_lock_init(&board_dat->data->lock);
- INIT_WORK(&board_dat->data->work, pch_spi_process_messages);
- init_waitqueue_head(&board_dat->data->wait);
-
- /* allocate resources for PCH SPI */
- retval = pch_spi_get_resources(board_dat);
- if (retval) {
- dev_err(&pdev->dev, "%s fail(retval=%d)\n", __func__, retval);
+ ret = pch_spi_get_resources(board_dat, data);
+ if (ret) {
+ dev_err(&plat_dev->dev, "%s fail(retval=%d)\n", __func__, ret);
goto err_spi_get_resources;
}
- dev_dbg(&pdev->dev, "%s pch_spi_get_resources returned=%d\n",
- __func__, retval);
-
- /* save private data in dev */
- pci_set_drvdata(pdev, board_dat);
- dev_dbg(&pdev->dev, "%s invoked pci_set_drvdata\n", __func__);
+ ret = request_irq(board_dat->pdev->irq, pch_spi_handler,
+ IRQF_SHARED, KBUILD_MODNAME, data);
+ if (ret) {
+ dev_err(&plat_dev->dev,
+ "%s request_irq failed\n", __func__);
+ goto err_request_irq;
+ }
+ data->irq_reg_sts = true;
- /* set master mode */
pch_spi_set_master_mode(master);
- dev_dbg(&pdev->dev,
- "%s invoked pch_spi_set_master_mode\n", __func__);
- /* Register the controller with the SPI core. */
- retval = spi_register_master(master);
- if (retval != 0) {
- dev_err(&pdev->dev,
+ ret = spi_register_master(master);
+ if (ret != 0) {
+ dev_err(&plat_dev->dev,
"%s spi_register_master FAILED\n", __func__);
- goto err_spi_reg_master;
+ goto err_spi_register_master;
}
- dev_dbg(&pdev->dev, "%s spi_register_master returned=%d\n",
- __func__, retval);
-
-
return 0;
-err_spi_reg_master:
- spi_unregister_master(master);
+err_spi_register_master:
+ free_irq(board_dat->pdev->irq, board_dat);
+err_request_irq:
+ pch_spi_free_resources(board_dat, data);
err_spi_get_resources:
-err_spi_alloc_master:
+ pci_iounmap(board_dat->pdev, data->io_remap_addr);
+err_pci_iomap:
spi_master_put(master);
- pci_disable_device(pdev);
-err_pci_en_device:
- kfree(board_dat);
-err_kmalloc:
- return retval;
+
+ return ret;
}
-static void pch_spi_remove(struct pci_dev *pdev)
+static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
{
- struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev);
+ struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
+ struct pch_spi_data *data = platform_get_drvdata(plat_dev);
int count;
- dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
-
- if (!board_dat) {
- dev_err(&pdev->dev,
- "%s pci_get_drvdata returned NULL\n", __func__);
- return;
- }
-
+ dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
+ __func__, plat_dev->id, board_dat->pdev->irq);
/* check for any pending messages; no action is taken if the queue
* is still full; but at least we tried. Unload anyway */
count = 500;
- spin_lock(&board_dat->data->lock);
- board_dat->data->status = STATUS_EXITING;
- while ((list_empty(&board_dat->data->queue) == 0) && --count) {
+ spin_lock(&data->lock);
+ data->status = STATUS_EXITING;
+ while ((list_empty(&data->queue) == 0) && --count) {
dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
__func__);
- spin_unlock(&board_dat->data->lock);
+ spin_unlock(&data->lock);
msleep(PCH_SLEEP_TIME);
- spin_lock(&board_dat->data->lock);
+ spin_lock(&data->lock);
}
- spin_unlock(&board_dat->data->lock);
-
- /* Free resources allocated for PCH SPI */
- pch_spi_free_resources(board_dat);
-
- spi_unregister_master(board_dat->data->master);
-
- /* free memory for private data */
- kfree(board_dat);
+ spin_unlock(&data->lock);
- pci_set_drvdata(pdev, NULL);
+ pch_spi_free_resources(board_dat, data);
+ /* disable interrupts & free IRQ */
+ if (data->irq_reg_sts) {
+ /* disable interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+ data->irq_reg_sts = false;
+ free_irq(board_dat->pdev->irq, data);
+ }
- /* disable PCI device */
- pci_disable_device(pdev);
+ pci_iounmap(board_dat->pdev, data->io_remap_addr);
+ spi_unregister_master(data->master);
+ spi_master_put(data->master);
+ platform_set_drvdata(plat_dev, NULL);
- dev_dbg(&pdev->dev, "%s invoked pci_disable_device\n", __func__);
+ return 0;
}
-
#ifdef CONFIG_PM
-static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state)
+static int pch_spi_pd_suspend(struct platform_device *pd_dev,
+ pm_message_t state)
{
u8 count;
- int retval;
-
- struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev);
+ struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
+ struct pch_spi_data *data = platform_get_drvdata(pd_dev);
- dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+ dev_dbg(&pd_dev->dev, "%s ENTRY\n", __func__);
if (!board_dat) {
- dev_err(&pdev->dev,
+ dev_err(&pd_dev->dev,
"%s pci_get_drvdata returned NULL\n", __func__);
return -EFAULT;
}
- retval = 0;
- board_dat->suspend_sts = true;
-
/* check if the current message is processed:
Only after thats done the transfer will be suspended */
count = 255;
- while ((--count) > 0) {
- if (!(board_dat->data->bcurrent_msg_processing)) {
- dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_"
- "msg_processing = false\n", __func__);
+ while ((--count) > 0)
+ if (!(data->bcurrent_msg_processing)) {
break;
- } else {
- dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_msg_"
- "processing = true\n", __func__);
- }
msleep(PCH_SLEEP_TIME);
}
/* Free IRQ */
- if (board_dat->irq_reg_sts) {
+ if (data->irq_reg_sts) {
/* disable all interrupts */
- pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
- PCH_ALL);
- pch_spi_reset(board_dat->data->master);
-
- free_irq(board_dat->pdev->irq, board_dat);
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+ pch_spi_reset(data->master);
+ free_irq(board_dat->pdev->irq, data);
- board_dat->irq_reg_sts = false;
- dev_dbg(&pdev->dev,
+ data->irq_reg_sts = false;
+ dev_dbg(&pd_dev->dev,
"%s free_irq invoked successfully.\n", __func__);
}
+ return 0;
+}
+
+static int pch_spi_pd_resume(struct platform_device *pd_dev)
+{
+ struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
+ struct pch_spi_data *data = platform_get_drvdata(pd_dev);
+ int retval;
+
+ if (!board_dat) {
+ dev_err(&pd_dev->dev,
+ "%s pci_get_drvdata returned NULL\n", __func__);
+ return -EFAULT;
+ }
+
+ if (!data->irq_reg_sts) {
+ /* register IRQ */
+ retval = request_irq(board_dat->pdev->irq, pch_spi_handler,
+ IRQF_SHARED, KBUILD_MODNAME, data);
+ if (retval < 0) {
+ dev_err(&pd_dev->dev,
+ "%s request_irq failed\n", __func__);
+ return retval;
+ }
+
+ /* reset PCH SPI h/w */
+ pch_spi_reset(data->master);
+ pch_spi_set_master_mode(data->master);
+ data->irq_reg_sts = true;
+ }
+ return 0;
+}
+#else
+#define pch_spi_pd_suspend NULL
+#define pch_spi_pd_resume NULL
+#endif
+
+static struct platform_driver pch_spi_pd_driver = {
+ .driver = {
+ .name = "pch-spi",
+ .owner = THIS_MODULE,
+ },
+ .probe = pch_spi_pd_probe,
+ .remove = __devexit_p(pch_spi_pd_remove),
+ .suspend = pch_spi_pd_suspend,
+ .resume = pch_spi_pd_resume
+};
+
+static int __devinit pch_spi_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct pch_spi_board_data *board_dat;
+ struct platform_device *pd_dev = NULL;
+ int retval;
+ int i;
+ struct pch_pd_dev_save *pd_dev_save;
+
+ pd_dev_save = kzalloc(sizeof(struct pch_pd_dev_save), GFP_KERNEL);
+ if (!pd_dev_save) {
+ dev_err(&pdev->dev, "%s Can't allocate pd_dev_sav\n", __func__);
+ return -ENOMEM;
+ }
+
+ board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
+ if (!board_dat) {
+ dev_err(&pdev->dev, "%s Can't allocate board_dat\n", __func__);
+ retval = -ENOMEM;
+ goto err_no_mem;
+ }
+
+ retval = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (retval) {
+ dev_err(&pdev->dev, "%s request_region failed\n", __func__);
+ goto pci_request_regions;
+ }
+
+ board_dat->pdev = pdev;
+ board_dat->num = id->driver_data;
+ pd_dev_save->num = id->driver_data;
+ pd_dev_save->board_dat = board_dat;
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ dev_err(&pdev->dev, "%s pci_enable_device failed\n", __func__);
+ goto pci_enable_device;
+ }
+
+ for (i = 0; i < board_dat->num; i++) {
+ pd_dev = platform_device_alloc("pch-spi", i);
+ if (!pd_dev) {
+ dev_err(&pdev->dev, "platform_device_alloc failed\n");
+ goto err_platform_device;
+ }
+ pd_dev_save->pd_save[i] = pd_dev;
+ pd_dev->dev.parent = &pdev->dev;
+
+ retval = platform_device_add_data(pd_dev, board_dat,
+ sizeof(*board_dat));
+ if (retval) {
+ dev_err(&pdev->dev,
+ "platform_device_add_data failed\n");
+ platform_device_put(pd_dev);
+ goto err_platform_device;
+ }
+
+ retval = platform_device_add(pd_dev);
+ if (retval) {
+ dev_err(&pdev->dev, "platform_device_add failed\n");
+ platform_device_put(pd_dev);
+ goto err_platform_device;
+ }
+ }
+
+ pci_set_drvdata(pdev, pd_dev_save);
+
+ return 0;
+
+err_platform_device:
+ pci_disable_device(pdev);
+pci_enable_device:
+ pci_release_regions(pdev);
+pci_request_regions:
+ kfree(board_dat);
+err_no_mem:
+ kfree(pd_dev_save);
+
+ return retval;
+}
+
+static void __devexit pch_spi_remove(struct pci_dev *pdev)
+{
+ int i;
+ struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "%s ENTRY:pdev=%p\n", __func__, pdev);
+
+ for (i = 0; i < pd_dev_save->num; i++)
+ platform_device_unregister(pd_dev_save->pd_save[i]);
+
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+ kfree(pd_dev_save->board_dat);
+ kfree(pd_dev_save);
+}
+
+#ifdef CONFIG_PM
+static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ int retval;
+ struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+
+ pd_dev_save->board_dat->suspend_sts = true;
+
/* save config space */
retval = pci_save_state(pdev);
-
if (retval == 0) {
- dev_dbg(&pdev->dev, "%s pci_save_state returned=%d\n",
- __func__, retval);
- /* disable PM notifications */
pci_enable_wake(pdev, PCI_D3hot, 0);
- dev_dbg(&pdev->dev,
- "%s pci_enable_wake invoked successfully\n", __func__);
- /* disable PCI device */
pci_disable_device(pdev);
- dev_dbg(&pdev->dev,
- "%s pci_disable_device invoked successfully\n",
- __func__);
- /* move device to D3hot state */
pci_set_power_state(pdev, PCI_D3hot);
- dev_dbg(&pdev->dev,
- "%s pci_set_power_state invoked successfully\n",
- __func__);
} else {
dev_err(&pdev->dev, "%s pci_save_state failed\n", __func__);
}
- dev_dbg(&pdev->dev, "%s return=%d\n", __func__, retval);
-
return retval;
}
static int pch_spi_resume(struct pci_dev *pdev)
{
int retval;
-
- struct pch_spi_board_data *board = pci_get_drvdata(pdev);
+ struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
- if (!board) {
- dev_err(&pdev->dev,
- "%s pci_get_drvdata returned NULL\n", __func__);
- return -EFAULT;
- }
-
- /* move device to DO power state */
pci_set_power_state(pdev, PCI_D0);
-
- /* restore state */
pci_restore_state(pdev);
retval = pci_enable_device(pdev);
@@ -1242,34 +1281,12 @@ static int pch_spi_resume(struct pci_dev *pdev)
dev_err(&pdev->dev,
"%s pci_enable_device failed\n", __func__);
} else {
- /* disable PM notifications */
pci_enable_wake(pdev, PCI_D3hot, 0);
- /* register IRQ handler */
- if (!board->irq_reg_sts) {
- /* register IRQ */
- retval = request_irq(board->pdev->irq, pch_spi_handler,
- IRQF_SHARED, KBUILD_MODNAME,
- board);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s request_irq failed\n", __func__);
- return retval;
- }
- board->irq_reg_sts = true;
-
- /* reset PCH SPI h/w */
- pch_spi_reset(board->data->master);
- pch_spi_set_master_mode(board->data->master);
-
- /* set suspend status to false */
- board->suspend_sts = false;
-
- }
+ /* set suspend status to false */
+ pd_dev_save->board_dat->suspend_sts = false;
}
- dev_dbg(&pdev->dev, "%s returning=%d\n", __func__, retval);
-
return retval;
}
#else
@@ -1289,15 +1306,25 @@ static struct pci_driver pch_spi_pcidev = {
static int __init pch_spi_init(void)
{
- return pci_register_driver(&pch_spi_pcidev);
+ int ret;
+ ret = platform_driver_register(&pch_spi_pd_driver);
+ if (ret)
+ return ret;
+
+ ret = pci_register_driver(&pch_spi_pcidev);
+ if (ret)
+ return ret;
+
+ return 0;
}
module_init(pch_spi_init);
static void __exit pch_spi_exit(void)
{
pci_unregister_driver(&pch_spi_pcidev);
+ platform_driver_unregister(&pch_spi_pd_driver);
}
module_exit(pch_spi_exit);
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Topcliff PCH SPI PCI Driver");
+MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI Driver");
Signed-off-by: Tomoya MORINAGA <tomoya...@dsn.okisemi.com>
---
drivers/spi/spi_topcliff_pch.c | 612 ++++++++++++++++++++++++++++++++--------
1 files changed, 492 insertions(+), 120 deletions(-)
diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
index 88bd472..f543ff6 100644
--- a/drivers/spi/spi_topcliff_pch.c
+++ b/drivers/spi/spi_topcliff_pch.c
@@ -28,6 +28,9 @@
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/pch_dma.h>
+
/* Register offsets */
#define PCH_SPCR 0x00 /* SPI control register */
#define PCH_SPBRR 0x04 /* SPI baud rate register */
@@ -36,7 +39,7 @@
#define PCH_SPDRR 0x10 /* SPI read data register */
#define PCH_SSNXCR 0x18 /* SSN Expand Control Register */
#define PCH_SRST 0x1C /* SPI reset register */
-#define PCH_SPI_ADDRESS_SIZE 0x20
+#define PCH_ADDRESS_SIZE 0x20
#define PCH_SPSR_TFD 0x000007C0
#define PCH_SPSR_RFD 0x0000F800
@@ -54,8 +57,6 @@
#define STATUS_EXITING 2
#define PCH_SLEEP_TIME 10
-#define PCH_ADDRESS_SIZE 0x20
-
#define SSN_LOW 0x02U
#define SSN_NO_CONTROL 0x00U
#define PCH_MAX_CS 0xFF
@@ -75,6 +76,7 @@
#define SPSR_TFI_BIT (1 << 0)
#define SPSR_RFI_BIT (1 << 1)
#define SPSR_FI_BIT (1 << 2)
+#define SPSR_ORF_BIT (1 << 3)
#define SPBRR_SIZE_BIT (1 << 10)
#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
@@ -83,10 +85,9 @@
#define SPCR_RFIC_FIELD 20
#define SPCR_TFIC_FIELD 16
-#define SPSR_INT_BITS 0x1F
-#define MASK_SPBRR_SPBR_BITS (~((1 << 10) - 1))
-#define MASK_RFIC_SPCR_BITS (~(0xf << 20))
-#define MASK_TFIC_SPCR_BITS (~(0xf000f << 12))
+#define MASK_SPBRR_SPBR_BITS ((1 << 10) - 1)
+#define MASK_RFIC_SPCR_BITS (0xf << SPCR_RFIC_FIELD)
+#define MASK_TFIC_SPCR_BITS (0xf << SPCR_TFIC_FIELD)
#define PCH_CLOCK_HZ 50000000
#define PCH_MAX_SPBR 1023
/**
* struct pch_spi_data - Holds the SPI channel specific details
* @io_remap_addr: The remapped PCI base address
@@ -140,6 +163,7 @@
*/
struct pch_spi_data {
void __iomem *io_remap_addr;
+ /* disable interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
/* transfer is completed;inform pch_spi_process_messages */
data->transfer_complete = true;
+ data->transfer_active = false;
wake_up(&data->wait);
}
}
@@ -316,11 +345,16 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
"%s returning due to suspend\n", __func__);
return IRQ_NONE;
}
+ if (data->use_dma)
+ return IRQ_NONE;
io_remap_addr = data->io_remap_addr;
spsr = io_remap_addr + PCH_SPSR;
reg_spsr_val = ioread32(spsr);
+ if (reg_spsr_val & SPSR_ORF_BIT)
+ dev_err(&board_dat->pdev->dev, "%s Over run error", __func__);
+
/* Check if the interrupt is for SPI device */
if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) {
pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr);
@@ -346,7 +380,7 @@ static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz)
if (n_spbr > PCH_MAX_SPBR)
n_spbr = PCH_MAX_SPBR;
- pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS);
+ pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, MASK_SPBRR_SPBR_BITS);
}
/**
@@ -454,26 +488,27 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
dev_dbg(&pspi->dev, "%s Transfer List not empty. "
"Transfer Speed is set.\n", __func__);
+ spin_lock_irqsave(&data->lock, flags);
/* validate Tx/Rx buffers and Transfer length */
list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
if (!transfer->tx_buf && !transfer->rx_buf) {
dev_err(&pspi->dev,
"%s Tx and Rx buffer NULL\n", __func__);
retval = -EINVAL;
- goto err_out;
+ goto err_return_spinlock;
}
if (!transfer->len) {
dev_err(&pspi->dev, "%s Transfer length invalid\n",
__func__);
@@ -566,8 +601,8 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
/* set bits per word if needed */
if (data->cur_trans->bits_per_word &&
- (data->current_msg->spi->bits_per_word !=\
- data->cur_trans->bits_per_word)) {
+ (data->current_msg->spi->bits_per_word !=\
+ data->cur_trans->bits_per_word)) {
dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__);
pch_spi_set_bits_per_word(data->master,
data->cur_trans->bits_per_word);
@@ -641,9 +676,9 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
data->transfer_active = true;
}
-static void pch_spi_nomore_transfer(struct pch_spi_data *data,
- struct spi_message *pmsg)
+static void pch_spi_nomore_transfer(struct pch_spi_data *data)
{
+ struct spi_message *pmsg;
dev_dbg(&data->master->dev, "%s called\n", __func__);
/* Invoke complete callback
* [To the spi core..indicating end of transfer] */
@@ -694,30 +729,21 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data,
static void pch_spi_set_ir(struct pch_spi_data *data)
- "%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
-
- /* SPI set enable */
- pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT,
- 0);
+ PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD |
+ SPCR_FIE_BIT | SPCR_ORIE_BIT |
+ SPCR_SPE_BIT,
+ MASK_RFIC_SPCR_BITS | PCH_ALL);
/* Wait until the transfer completes; go to sleep after
initiating the transfer. */
@@ -730,15 +756,13 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
dev_dbg(&data->master->dev,
"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
- data->transfer_active = false;
- dev_dbg(&data->master->dev,
- "%s set data->transfer_active = false\n", __func__);
-
/* clear all interrupts */
pch_spi_writereg(data->master, PCH_SPSR,
pch_spi_readreg(data->master, PCH_SPSR));
- /* disable interrupts */
- pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+ /* Disable interrupts and SPI transfer */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL | SPCR_SPE_BIT);
+ /* clear FIFO */
+ pch_spi_clear_fifo(data->master);
}
static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
+ /* clear fifo threshold, disable interrupts, disable SPI transfer */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
+ /* set bits per word if needed */
+ if (data->cur_trans->bits_per_word &&
+ (data->current_msg->spi->bits_per_word !=
+ data->cur_trans->bits_per_word)) {
+ /* set receive fifo threshold and transmit fifo threshold */
+ pch_spi_setclr_reg(data->master, PCH_SPCR,
+ "0x2 to SSNXCR\n", __func__);
+
+ spin_lock_irqsave(&data->lock, flags);
+ pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW);
+ desc_rx->tx_submit(desc_rx);
+ desc_tx->tx_submit(desc_tx);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* reset transfer complete flag */
+ data->transfer_complete = false;
+}
+
static void pch_spi_process_messages(struct work_struct *pwork)
{
struct spi_message *pmsg;
@@ -772,30 +1118,23 @@ static void pch_spi_process_messages(struct work_struct *pwork)
dev_dbg(&data->master->dev, "%s data initialized\n", __func__);
spin_lock(&data->lock);
-
/* check if suspend has been initiated;if yes flush queue */
if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) {
- dev_dbg(&data->master->dev,
- "%s suspend/remove initiated,flushing queue\n",
- __func__);
-
-
} while (data->cur_trans != NULL);
+
+ if (data->use_dma)
+ pch_spi_release_dma(data);
}
static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
@@ -911,6 +1236,7 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
/* create workqueue */
data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+
if (!data->wk) {
dev_err(&board_dat->pdev->dev,
"%s create_singlet hread_workqueue failed\n", __func__);
@@ -937,6 +1263,35 @@ err_return:
return retval;
}
+static void pch_free_dma_buf(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
+{
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+ if (dma->tx_buf_dma)
+ dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+ dma->tx_buf_virt, dma->tx_buf_dma);
+ if (dma->rx_buf_dma)
+ dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+ dma->rx_buf_virt, dma->rx_buf_dma);
+ return;
+}
+
+static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
+{
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+ /* Get Consistent memory for Tx DMA */
+ dma->tx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+ PCH_BUF_SIZE, &dma->tx_buf_dma, GFP_KERNEL);
+ /* Get Consistent memory for Rx DMA */
+ dma->rx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+ PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL);
+}
+
static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
{
int ret;
@@ -944,6 +1299,8 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
struct pch_spi_data *data;
+ dev_dbg(&plat_dev->dev, "%s:debug\n", __func__);
+
master = spi_alloc_master(&board_dat->pdev->dev,
sizeof(struct pch_spi_data));
if (!master) {
@@ -957,9 +1314,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
platform_set_drvdata(plat_dev, data);
- /* baseaddress + 0x20(offset) */
+ /* baseaddress + address offset) */
+ data->io_base_addr = pci_resource_start(board_dat->pdev, 1) +
+ PCH_ADDRESS_SIZE * plat_dev->id;
data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
- 0x20 * plat_dev->id;
+ PCH_ADDRESS_SIZE * plat_dev->id;
if (!data->io_remap_addr) {
dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
ret = -ENOMEM;
@@ -980,6 +1339,7 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
data->n_curnt_chip = 255;
data->status = STATUS_RUNNING;
data->ch = plat_dev->id;
+ data->use_dma = use_dma;
INIT_LIST_HEAD(&data->queue);
spin_lock_init(&data->lock);
@@ -1010,6 +1370,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
goto err_spi_register_master;
}
+ if (use_dma) {
+ dev_info(&plat_dev->dev, "Use DMA for data transfers\n");
+ pch_alloc_dma_buf(board_dat, data);
+ }
+
return 0;
err_spi_register_master:
@@ -1029,22 +1394,27 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
struct pch_spi_data *data = platform_get_drvdata(plat_dev);
int count;
+ unsigned long flags;
dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
__func__, plat_dev->id, board_dat->pdev->irq);
+
+ if (use_dma)
+ pch_free_dma_buf(board_dat, data);
+
/* check for any pending messages; no action is taken if the queue
* is still full; but at least we tried. Unload anyway */
count = 500;
- spin_lock(&data->lock);
+ spin_lock_irqsave(&data->lock, flags);
data->status = STATUS_EXITING;
while ((list_empty(&data->queue) == 0) && --count) {
dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
__func__);
- spin_unlock(&data->lock);
+ spin_unlock_irqrestore(&data->lock, flags);
msleep(PCH_SLEEP_TIME);
- spin_lock(&data->lock);
+ spin_lock_irqsave(&data->lock, flags);
}
- spin_unlock(&data->lock);
+ spin_unlock_irqrestore(&data->lock, flags);
pch_spi_free_resources(board_dat, data);
/* disable interrupts & free IRQ */
@@ -1081,8 +1451,8 @@ static int pch_spi_pd_suspend(struct platform_device *pd_dev,
/* check if the current message is processed:
Only after thats done the transfer will be suspended */
count = 255;
- while ((--count) > 0)
- if (!(data->bcurrent_msg_processing)) {
+ while ((--count) > 0) {
+ if (!(data->bcurrent_msg_processing))
break;
msleep(PCH_SLEEP_TIME);
}
@@ -1123,7 +1493,6 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
"%s request_irq failed\n", __func__);
return retval;
}
-
/* reset PCH SPI h/w */
pch_spi_reset(data->master);
pch_spi_set_master_mode(data->master);
@@ -1213,7 +1582,6 @@ static int __devinit pch_spi_probe(struct pci_dev *pdev,
}
pci_set_drvdata(pdev, pd_dev_save);
-
return 0;
err_platform_device:
@@ -1326,5 +1694,9 @@ static void __exit pch_spi_exit(void)
}
module_exit(pch_spi_exit);
+module_param(use_dma, int, 0644);
+MODULE_PARM_DESC(use_dma,
+ "to use DMA for data transfers pass 1 else 0; default 1");
+
MODULE_LICENSE("GPL");
Support ML7213 device of OKI SEMICONDUCTOR.
ML7213 is companion chip of Intel Atom E6xx series for IVI(In-Vehicle Infotainment).
ML7213 is compatible for Intel EG20T PCH.
Signed-off-by: Tomoya MORINAGA <tomoya...@dsn.okisemi.com>
---
drivers/spi/Kconfig | 5 +-
drivers/spi/spi_topcliff_pch.c | 599 +++++++++++++++++++++-------------------
2 files changed, 317 insertions(+), 287 deletions(-)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index fc14b8d..ed6134b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -380,12 +380,15 @@ config SPI_TI_SSP
module will be called ti-ssp-spi.
config SPI_TOPCLIFF_PCH
- tristate "Topcliff PCH SPI Controller"
+ tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI controller"
depends on PCI
help
SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus
used in some x86 embedded processors.
+ This driver also supports the ML7213, a companion chip for the
+ Atom E6xx series and compatible with the Intel EG20T PCH.
+
config SPI_TXX9
tristate "Toshiba TXx9 SPI controller"
depends on GENERIC_GPIO && CPU_TX49XX
diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
index 79e48d4..88bd472 100644
--- a/drivers/spi/spi_topcliff_pch.c
+++ b/drivers/spi/spi_topcliff_pch.c
@@ -26,6 +26,7 @@
#include <linux/spi/spidev.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/platform_device.h>
/* Register offsets */
#define PCH_SPCR 0x00 /* SPI control register */
@@ -35,6 +36,7 @@
#define PCH_SPDRR 0x10 /* SPI read data register */
#define PCH_SSNXCR 0x18 /* SSN Expand Control Register */
#define PCH_SRST 0x1C /* SPI reset register */
+#define PCH_SPI_ADDRESS_SIZE 0x20
#define PCH_SPSR_TFD 0x000007C0
#define PCH_SPSR_RFD 0x0000F800
@@ -75,7 +77,8 @@
#define SPSR_FI_BIT (1 << 2)
#define SPBRR_SIZE_BIT (1 << 10)
-#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|SPCR_ORIE_BIT|SPCR_MDFIE_BIT)
+#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
+ SPCR_ORIE_BIT|SPCR_MDFIE_BIT)
#define SPCR_RFIC_FIELD 20
#define SPCR_TFIC_FIELD 16
@@ -88,6 +91,16 @@
#define PCH_CLOCK_HZ 50000000
#define PCH_MAX_SPBR 1023
+/* Definition for ML7213 by OKI SEMICONDUCTOR */
+#define PCI_VENDOR_ID_ROHM 0x10DB
+#define PCI_DEVICE_ID_ML7213_SPI 0x802c
+
+/*
+ * Set the number of SPI instance max
+ * Intel EG20T PCH : 1ch
+ * OKI SEMICONDUCTOR ML7213 IOH : 2ch
+*/
+#define PCH_SPI_MAX_DEV 2
/**
* struct pch_spi_data - Holds the SPI channel specific details
@@ -121,6 +134,9 @@
* @cur_trans: The current transfer that this SPI driver is
* handling
* @board_dat: Reference to the SPI device data structure
+ * @plat_dev: platform_device structure
+ * @ch: SPI channel number
+ * @irq_reg_sts: Status of IRQ registration
*/
struct pch_spi_data {
void __iomem *io_remap_addr;
@@ -283,11 +305,11 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
static irqreturn_t pch_spi_handler(int irq, void *dev_id)
{
u32 reg_spsr_val;
- struct pch_spi_data *data;
void __iomem *spsr;
void __iomem *io_remap_addr;
irqreturn_t ret = IRQ_NONE;
- struct pch_spi_board_data *board_dat = dev_id;
+ struct pch_spi_data *data = dev_id;
+ struct pch_spi_board_data *board_dat = data->board_dat;
if (board_dat->suspend_sts) {
dev_dbg(&board_dat->pdev->dev,
@@ -295,10 +317,8 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
return IRQ_NONE;
}
- data = board_dat->data;
io_remap_addr = data->io_remap_addr;
spsr = io_remap_addr + PCH_SPSR;
-
reg_spsr_val = ioread32(spsr);
/* Check if the interrupt is for SPI device */
@@ -412,7 +432,6 @@ static int pch_spi_setup(struct spi_device *pspi)
static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
{
-
struct spi_transfer *transfer;
struct pch_spi_data *data = spi_master_get_devdata(pspi->master);
int retval;
@@ -547,7 +566,8 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
/* set bits per word if needed */
if (data->cur_trans->bits_per_word &&
- (data->current_msg->spi->bits_per_word != data->cur_trans->bits_per_word)) {
+ (data->current_msg->spi->bits_per_word !=\
+ data->cur_trans->bits_per_word)) {
dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__);
pch_spi_set_bits_per_word(data->master,
data->cur_trans->bits_per_word);
@@ -621,7 +641,6 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
data->transfer_active = true;
}
-
static void pch_spi_nomore_transfer(struct pch_spi_data *data,
struct spi_message *pmsg)
{
@@ -697,7 +716,8 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
"%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
/* SPI set enable */
- pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT, 0);
+ pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT,
+ 0);
/* Wait until the transfer completes; go to sleep after
initiating the transfer. */
@@ -742,7 +762,6 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
}
}
-
static void pch_spi_process_messages(struct work_struct *pwork)
{
struct spi_message *pmsg;
@@ -868,117 +887,49 @@ static void pch_spi_process_messages(struct work_struct *pwork)
} while (data->cur_trans != NULL);
}
-static void pch_spi_free_resources(struct pch_spi_board_data *board_dat)
+static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
{
dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
/* free workqueue */
- if (board_dat->data->wk != NULL) {
- destroy_workqueue(board_dat->data->wk);
- board_dat->data->wk = NULL;
+ if (data->wk != NULL) {
+ destroy_workqueue(data->wk);
+ data->wk = NULL;
dev_dbg(&board_dat->pdev->dev,
"%s destroy_workqueue invoked successfully\n",
__func__);
}
-
- /* disable interrupts & free IRQ */
- if (board_dat->irq_reg_sts) {
- /* disable interrupts */
- pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
- PCH_ALL);
-
- /* free IRQ */
- free_irq(board_dat->pdev->irq, board_dat);
-
- dev_dbg(&board_dat->pdev->dev,
- "%s free_irq invoked successfully\n", __func__);
-
- board_dat->irq_reg_sts = false;
- }
-
- /* unmap PCI base address */
- if (board_dat->data->io_remap_addr != 0) {
- pci_iounmap(board_dat->pdev, board_dat->data->io_remap_addr);
-
- board_dat->data->io_remap_addr = 0;
-
- dev_dbg(&board_dat->pdev->dev,
- "%s pci_iounmap invoked successfully\n", __func__);
- }
-
- /* release PCI region */
- if (board_dat->pci_req_sts) {
- pci_release_regions(board_dat->pdev);
- dev_dbg(&board_dat->pdev->dev,
- "%s pci_release_regions invoked successfully\n",
- __func__);
- board_dat->pci_req_sts = false;
- }
}
-static int pch_spi_get_resources(struct pch_spi_board_data *board_dat)
+static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
{
- void __iomem *io_remap_addr;
- int retval;
+ int retval = 0;
+
dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
/* create workqueue */
- board_dat->data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
- if (!board_dat->data->wk) {
+ data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+ if (!data->wk) {
dev_err(&board_dat->pdev->dev,
"%s create_singlet hread_workqueue failed\n", __func__);
retval = -EBUSY;
goto err_return;
}
- dev_dbg(&board_dat->pdev->dev,
- "%s create_singlethread_workqueue success\n", __func__);
-
- retval = pci_request_regions(board_dat->pdev, KBUILD_MODNAME);
- if (retval != 0) {
- dev_err(&board_dat->pdev->dev,
- "%s request_region failed\n", __func__);
- goto err_return;
- }
-
- board_dat->pci_req_sts = true;
-
- io_remap_addr = pci_iomap(board_dat->pdev, 1, 0);
- if (io_remap_addr == 0) {
- dev_err(&board_dat->pdev->dev,
- "%s pci_iomap failed\n", __func__);
- retval = -ENOMEM;
- goto err_return;
- }
-
- /* calculate base address for all channels */
- board_dat->data->io_remap_addr = io_remap_addr;
-
/* reset PCH SPI h/w */
- dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
-
- /* allocate memory for private data */
- board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
- if (board_dat == NULL) {
- dev_err(&pdev->dev,
- " %s memory allocation for private data failed\n",
- __func__);
- retval = -ENOMEM;
- goto err_kmalloc;
- }
-
- dev_dbg(&pdev->dev,
- "%s memory allocation for private data success\n", __func__);
-
- /* enable PCI device */
- retval = pci_enable_device(pdev);
- if (retval != 0) {
- dev_err(&pdev->dev, "%s pci_enable_device FAILED\n", __func__);
-
- goto err_pci_en_device;
+ master = spi_alloc_master(&board_dat->pdev->dev,
- dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
-
- if (!board_dat) {
- dev_err(&pdev->dev,
- "%s pci_get_drvdata returned NULL\n", __func__);
- return;
- }
-
+ dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
+ __func__, plat_dev->id, board_dat->pdev->irq);
/* check for any pending messages; no action is taken if the queue
* is still full; but at least we tried. Unload anyway */
count = 500;
- spin_lock(&board_dat->data->lock);
- board_dat->data->status = STATUS_EXITING;
- while ((list_empty(&board_dat->data->queue) == 0) && --count) {
+ spin_lock(&data->lock);
+ data->status = STATUS_EXITING;
+ while ((list_empty(&data->queue) == 0) && --count) {
dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
__func__);
- spin_unlock(&board_dat->data->lock);
+ spin_unlock(&data->lock);
msleep(PCH_SLEEP_TIME);
- spin_lock(&board_dat->data->lock);
+ spin_lock(&data->lock);
}
- spin_unlock(&board_dat->data->lock);
-
- /* Free resources allocated for PCH SPI */
- pch_spi_free_resources(board_dat);
-
- spi_unregister_master(board_dat->data->master);
-
- /* free memory for private data */
- kfree(board_dat);
+ spin_unlock(&data->lock);
- pci_set_drvdata(pdev, NULL);
+ pch_spi_free_resources(board_dat, data);
+ /* disable interrupts & free IRQ */
+ if (data->irq_reg_sts) {
+ /* disable interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
/* check if the current message is processed:
Only after thats done the transfer will be suspended */
count = 255;
- while ((--count) > 0) {
- if (!(board_dat->data->bcurrent_msg_processing)) {
- dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_"
- "msg_processing = false\n", __func__);
+ while ((--count) > 0)
+ if (!(data->bcurrent_msg_processing)) {
break;
- } else {
- dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_msg_"
- "processing = true\n", __func__);
- }
msleep(PCH_SLEEP_TIME);
}
/* Free IRQ */
- if (board_dat->irq_reg_sts) {
+ if (data->irq_reg_sts) {
/* disable all interrupts */
- pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
- PCH_ALL);
- pch_spi_reset(board_dat->data->master);
-
- free_irq(board_dat->pdev->irq, board_dat);
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+ dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+
+ pd_dev_save->board_dat->suspend_sts = true;
+
/* save config space */
retval = pci_save_state(pdev);
-
if (retval == 0) {
- dev_dbg(&pdev->dev, "%s pci_save_state returned=%d\n",
- __func__, retval);
- /* disable PM notifications */
pci_enable_wake(pdev, PCI_D3hot, 0);
- dev_dbg(&pdev->dev,
- "%s pci_enable_wake invoked successfully\n", __func__);
- /* disable PCI device */
pci_disable_device(pdev);
- dev_dbg(&pdev->dev,
- "%s pci_disable_device invoked successfully\n",
- __func__);
- /* reset PCH SPI h/w */
+MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI Driver");
It seems My PC's clock is reset.
> depends on GENERIC_GPIO&& CPU_TX49XX
> - /* disable interrupts& free IRQ */
> - while ((list_empty(&board_dat->data->queue) == 0)&& --count) {
> + spin_lock(&data->lock);
> + data->status = STATUS_EXITING;
> + while ((list_empty(&data->queue) == 0)&& --count) {
> dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
> __func__);
> - spin_unlock(&board_dat->data->lock);
> + spin_unlock(&data->lock);
> msleep(PCH_SLEEP_TIME);
> - spin_lock(&board_dat->data->lock);
> + spin_lock(&data->lock);
> }
> - spin_unlock(&board_dat->data->lock);
> -
> - /* Free resources allocated for PCH SPI */
> - pch_spi_free_resources(board_dat);
> -
> - spi_unregister_master(board_dat->data->master);
> -
> - /* free memory for private data */
> - kfree(board_dat);
> + spin_unlock(&data->lock);
>
> - pci_set_drvdata(pdev, NULL);
> + pch_spi_free_resources(board_dat, data);
> + /* disable interrupts& free IRQ */
> + pd_dev->dev.parent =&pdev->dev;
--
tomoya
OKI SEMICONDUCTOR CO., LTD.
Why don't you give us your comment/opinion at all for our v4 patch ??
We have been long waiting since 27-April.
(2011/06/07 14:50), Tomoya MORINAGA wrote:
> ***Modify Grant's comments.
> - Delete unrelated whitespace
> - Prevent device driver from accessing platform data
> - Add __devinit and __devexit
> - Save pdev->dev to pd_dev->dev.parent
> - Have own suspend/resume processing in platform_driver.
> - Care returned value in pch_spi_init
> - Change unregister order
>
> Support ML7213 device of OKI SEMICONDUCTOR.
> ML7213 is companion chip of Intel Atom E6xx series for IVI(In-Vehicle Infotainment).
> ML7213 is compatible for Intel EG20T PCH.
>
> Signed-off-by: Tomoya MORINAGA<tomoya...@dsn.okisemi.com>
> ---
> drivers/spi/Kconfig | 5 +-
> drivers/spi/spi_topcliff_pch.c | 599 +++++++++++++++++++++-------------------
> 2 files changed, 317 insertions(+), 287 deletions(-)
>
--
tomoya
OKI SEMICONDUCTOR CO., LTD.
Sorry about that Tomoya. I got completely overloaded at the end of
April and all of May. I ended up dropping a lot of patches on the
floor because of it. I'm not purposefully ignoring you!
I'll look at them now.
g.
Hi Tomoya,
comment below...
This shouldn't be here. The bus id should be dynamically allocated,
and using the plat_dev->id assumes that there are no other spi busses
in the system, which is a bad assumption.
I picked up the patch (it's about time I guess, I've left this out
alone for too long), but I've dropped this hunk.
You can post a followup patch if it broke anything.
g.
Merged thanks.
BTW, please be aware and careful of whitespace changes. There were
quite a few unrelated hunks in both this patch and the last one that
have no functional change, and just make it harder to go through the
real changes. Basically, unless you're touching the line immediately
before or immediately after, don't cleanup whitespace in patches with
functional changes.
Also, I've not reviewed this driver very carefully, so I haven't
inspected the DMA code for correctness.
g.
> Sorry about that Tomoya. I got completely overloaded at the end of
> April and all of May. I ended up dropping a lot of patches on the
> floor because of it. I'm not purposefully ignoring you!
I can understand you ware very busy last month.
Though I thought you tried to avoid me,
I was relieved to read this your mail.
I wanted to know the information at last month.
Thanks,
tomoya